Насколько медленны исключения Java?

Я предпочитаю это, потому что он все еще возвращает TRUE, когда он преуспевает, и FALSE, когда он терпит неудачу, а также предотвращает ошибку, в которой пустые пути могут попробовать и удалить все из «/ *» !!:

function deleteDir($path)
{
    return !empty($path) && is_file($path) ?
        @unlink($path) :
        (array_reduce(glob($path.'/*'), function ($r, $i) { return $r && deleteDir($i); }, TRUE)) && @rmdir($path);
}
459
задан Yoon5oo 30 July 2018 в 19:12
поделиться

7 ответов

Это зависит, как реализованы исключения. Самый простой путь использует setjmp и longjmp. Это означает, что все регистры ЦП записаны в стек (который уже занимает время), и возможно некоторые другие данные должны быть созданы..., все это уже происходит в операторе попытки. Оператор броска должен раскрутить стек и восстановить значения всех регистров (и возможные другие значения в VM). Поэтому попытайтесь бросить, являются одинаково медленными, и это довольно медленно, однако если никакое исключение не выдается, выходить из блока попытки не занимает времени вообще в большинстве случаев (поскольку все помещается на стек, который моется автоматически, если метод существует).

Sun и другие распознали, что это является возможно субоптимальным, и конечно VMs становятся быстрее и быстрее за время. Существует другой способ реализовать исключения, который делает саму попытку молнией быстро (на самом деле, ничего не происходит для попытки вообще в целом - все, что должно произойти, уже сделан, когда класс загружается VM), и это делает бросок не совсем как медленный. Я не знаю, какая JVM использует эту новую, лучшую технику...

..., но Вы пишете в Java, таким образом, Ваш код позже только работает на одной JVM в одной определенной системе? С тех пор, если это может когда-либо работать на какой-либо другой платформе или какой-либо другой версии JVM (возможно какого-либо другого поставщика), кто говорит, они также используют внедрение FAST? Быстрый более сложен, чем медленный и не легко возможен во всех системах. Вы хотите остаться портативными? Тогда не полагайтесь на исключения, являющиеся быстрым.

Это также имеет большое значение, что Вы делаете в блоке попытки. Если Вы откроете блок попытки и никогда не называть метода из этого блока попытки, блок попытки будет крайний быстрый, поскольку JIT может тогда на самом деле рассматривать бросок как простой goto. Это ни не должно сохранять состояние стека, и при этом это не должно раскручивать стек, если исключение выдается (это только должно перейти к обработчикам выгод). Однако это не то, что Вы обычно делаете. Обычно Вы открываете блок попытки и затем называете метод, который мог бы выдать исключение, правильно? И даже если Вы просто используете блок попытки в рамках своего метода, каким методом это будет, который не называет никакой другой метод? Это просто вычислит число? Тогда, чему для Вас нужны исключения? Существуют намного более изящные способы отрегулировать процесс выполнения программы. Для в значительной степени чего-либо еще кроме простой математики, необходимо будет назвать внешний метод, и это уже уничтожает преимущество локального блока попытки.

Посмотрите следующий тестовый код:

public class Test {
    int value;


    public int getValue() {
        return value;
    }

    public void reset() {
        value = 0;
    }

    // Calculates without exception
    public void method1(int i) {
        value = ((value + i) / i) << 1;
        // Will never be true
        if ((i & 0xFFFFFFF) == 1000000000) {
            System.out.println("You'll never see this!");
        }
    }

    // Could in theory throw one, but never will
    public void method2(int i) throws Exception {
        value = ((value + i) / i) << 1;
        // Will never be true
        if ((i & 0xFFFFFFF) == 1000000000) {
            throw new Exception();
        }
    }

    // This one will regularly throw one
    public void method3(int i) throws Exception {
        value = ((value + i) / i) << 1;
        // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
        // an AND operation between two integers. The size of the number plays
        // no role. AND on 32 BIT always ANDs all 32 bits
        if ((i & 0x1) == 1) {
            throw new Exception();
        }
    }

    public static void main(String[] args) {
        int i;
        long l;
        Test t = new Test();

        l = System.currentTimeMillis();
        t.reset();
        for (i = 1; i < 100000000; i++) {
            t.method1(i);
        }
        l = System.currentTimeMillis() - l;
        System.out.println(
            "method1 took " + l + " ms, result was " + t.getValue()
        );

        l = System.currentTimeMillis();
        t.reset();
        for (i = 1; i < 100000000; i++) {
            try {
                t.method2(i);
            } catch (Exception e) {
                System.out.println("You'll never see this!");
            }
        }
        l = System.currentTimeMillis() - l;
        System.out.println(
            "method2 took " + l + " ms, result was " + t.getValue()
        );

        l = System.currentTimeMillis();
        t.reset();
        for (i = 1; i < 100000000; i++) {
            try {
                t.method3(i);
            } catch (Exception e) {
                // Do nothing here, as we will get here
            }
        }
        l = System.currentTimeMillis() - l;
        System.out.println(
            "method3 took " + l + " ms, result was " + t.getValue()
        );
    }
}

Результат:

method1 took 972 ms, result was 2
method2 took 1003 ms, result was 2
method3 took 66716 ms, result was 2

замедление от блока попытки является слишком маленьким для исключения факторов соединения, таких как фоновые процессы. Но блок выгоды уничтожил все и сделал его в 66 раз медленнее!

, Поскольку я сказал, результат не будет состоять в том, что плохо, если Вы помещаете попытку/выгоду и бросаете все в рамках того же метода (method3), но это - специальная оптимизация JIT, на которую я не положился бы. И даже когда с помощью этой оптимизации, бросок является все еще довольно медленным. Таким образом, я не знаю то, что Вы пытаетесь сделать здесь, но существует определенно лучший способ сделать его, чем использование попытки/выгоды/броска.

331
ответ дан Pr0methean 30 July 2018 в 19:12
поделиться
  • 1
    @briandfoy Ваша справка комментариев, но Вы знаете то, что Вы забыли делать? укажите, который делает работа для неравноценно измеренных массивов. (I' m делающий покупки тот, который должен сделать это), – Steven Lu 27 August 2013 в 22:12

Почему исключения должны быть немного медленнее, чем нормальные возвраты?

, пока Вы не печатаете stacktrace к терминалу, сохраните его в файл или что-то подобное, блок выгоды больше не делает работы, чем другие блоки кода. Так, я не могу вообразить, почему "бросок новый my_cool_error ()" должен быть, это замедляется.

Хороший вопрос и я с нетерпением ждем дополнительной информации об этой теме!

-5
ответ дан ljs.dev 30 July 2018 в 19:12
поделиться

HotSpot довольно способен к удалению кода исключения для исключений, генерируемых системой, пока это все встраивается. Однако явно созданное исключение и иначе не удаленные проводят много времени, создавая отслеживание стека. Переопределение fillInStackTrace, чтобы видеть, как это может влиять на производительность.

3
ответ дан Tom Hawtin - tackline 30 July 2018 в 19:12
поделиться
  • 1
    It' s очень опасный для вывода вводимого пользователем текста это hasn' t закодированный. Кодирование должно быть поощрено даже при том, что это wasn' t используемый в исходном вопросе. – David Glenn 17 February 2011 в 18:13

Даже если выдача исключения не является медленной, это - все еще плохая идея выдать исключения для нормального процесса выполнения программы. Используемый этот путь это походит на GOTO...

я предполагаю, что это действительно не отвечает на вопрос все же. Я предположил бы, что 'стандартная' мудрость выдающих исключения, будучи медленной была верна в более ранних версиях Java (< 1.4). Создание исключения требует, чтобы VM создали все отслеживание стека. Много изменилось с тех пор в VM для ускорения вещей, и это вероятно одна область, которая была улучшена.

3
ответ дан user38051 30 July 2018 в 19:12
поделиться
  • 1
    почему сделал этот получает большинство голосов? Действительно ли это является самым эффективным или самым безопасным? – Shawn Mclean 17 February 2011 в 20:28

Я думаю, что первая статья относится к действию пересечения стека вызовов и создания отслеживания стека, как являющегося дорогой частью, и в то время как во второй статье не говорится это, я думаю, что это - самая дорогая часть создания объекта. John Rose имеет статья, где он описывает различные методы для ускорения исключений . (Предварительное выделение и многократное использование исключения, исключений без отслеживаний стека, и т.д.)

, Но все еще - я думаю, что это нужно считать только необходимым злом, последним средством. Причина John того, чтобы сделать это состоит в том, чтобы эмулировать функции на других языках, которые еще не доступны в JVM. Вы не должны вырабатывать привычку использования исключений для потока управления. Особенно не по причинам производительности! Как Вы сами упоминаете в № 2, Вы рискуете маскировать серьезные ошибки в своем коде этот путь, и будет более трудно поддержать для новых программистов.

в Микросравнительных тестах в Java удивительно трудно разобраться (мне сказали), особенно когда Вы входите в территорию JIT, таким образом, я действительно сомневаюсь, что использование исключений быстрее, чем "возврат" в реальной жизни. Например, я подозреваю, что Вы имеете где-нибудь между 2 и 5 стековыми фреймами в Вашем тесте? Теперь предположите, что Ваш код будет вызван компонентом JSF, развернутым JBoss. Теперь у Вас могло бы быть отслеживание стека, которое несколько страниц длиной.

, Возможно, Вы могли отправить свой тестовый код?

7
ответ дан jrudolph 30 July 2018 в 19:12
поделиться

Некоторое время назад я записал класс для проверения относительной производительности преобразования строк к ints использование двух подходов: (1) назовите Integer.parseInt () и поймайте исключение, или (2) соответствуйте строке regex и назовите parseInt (), только если соответствие успешно выполняется. Я использовал regex самым эффективным способом, которым я мог (т.е. создание объектов Pattern и Matcher прежде, чем хоронить цикл), и я не распечатал или сохранил stacktraces от исключений.

Для списка десяти тысяч строк, если они были всеми верными номерами parseInt () подход был четыре раза с такой скоростью, как подход regex. Но если только 80% строк были допустимы, regex был дважды с такой скоростью, как parseInt (). И если 20% были допустимы, означая, что исключение было выдано и поймало 80% времени, regex был приблизительно двадцать раз с такой скоростью, как parseInt ().

я был удивлен результатом, полагая, что подход regex обрабатывает допустимые строки дважды: однажды для соответствия и снова для parseInt (). Но бросок и ловля исключений, более, чем восполненных это. Этот вид ситуации, вероятно, не будет происходить очень часто в реальном мире, но если это делает, Вы определенно не должны использовать ловящую исключение технику. Но если Вы только проверяете ввод данных пользователем или что-то как этот, любой ценой используйте parseInt () подход.

7
ответ дан Alan Moore 30 July 2018 в 19:12
поделиться
  • 1
    Извинения за поощрение кодирования в моем комментарии и затем пропущения это в моем собственном коде. Сбой. – David Glenn 17 February 2011 в 18:16

Я сделал некоторое тестирование производительности с JVM 1.5, и исключения использования был, по крайней мере, 2x медленнее. В среднем: Время выполнения на тривиально маленьком методе, более чем утроенном (3x) за исключениями. Тривиально маленький цикл, который должен был поймать исключение, видел 2x увеличение в самовремя.

я видел подобные числа в производственном коде, а также микро сравнительных тестах.

Исключения должны определенно НЕ использоваться для чего-либо, чем это часто называют. При броске тысячи исключений в секунду вызвали бы огромное горлышко бутылки.

, Например, с помощью "Целое число. ParseInt (...)" для нахождения всех плохих значений в файле очень крупного текста - очень плохая идея. (Я видел, что этот служебный метод уничтожает производительность на производственном коде)

Используя исключение для создания отчетов о плохом значении относительно пользовательской формы графического интерфейса, вероятно, не настолько плохо с точки зрения производительности.

, Является ли это хорошей практикой дизайна, я пошел бы с правилом: если ошибка нормальный/ожидает, то используйте возвращаемое значение. Если это является аварийным, используйте исключение. Например: чтение вводов данных пользователем, плохие значения нормальны - используют код ошибки. При передаче значения внутренней служебной функции плохие значения должны быть фильтрованы кодом вызова - используют исключение.

6
ответ дан James Schek 30 July 2018 в 19:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: