Стоимость создания исключений C ++ 0x - PullRequest
14 голосов
/ 19 июня 2009

Какое влияние на производительность выдает исключение в C ++ 0x? Насколько зависит этот компилятор? Это не то же самое, что спрашивать , какова стоимость ввода блока try, даже если не выдается исключение .

Должны ли мы ожидать более широкого использования исключений для общей логической обработки, как в Java?

Ответы [ 9 ]

36 голосов
/ 19 июня 2009
#include <iostream>
#include <stdexcept>

struct SpaceWaster {
    SpaceWaster(int l, SpaceWaster *p) : level(l), prev(p) {}
    // we want the destructor to do something
    ~SpaceWaster() { prev = 0; }
    bool checkLevel() { return level == 0; }
    int level;
    SpaceWaster *prev;
};

void thrower(SpaceWaster *current) {
    if (current->checkLevel()) throw std::logic_error("some error message goes here\n");
    SpaceWaster next(current->level - 1, current);
    // typical exception-using code doesn't need error return values
    thrower(&next);
    return;
}

int returner(SpaceWaster *current) {
    if (current->checkLevel()) return -1;
    SpaceWaster next(current->level - 1, current);
    // typical exception-free code requires that return values be handled
    if (returner(&next) == -1) return -1;
    return 0;
}

int main() {
    const int repeats = 1001;
    int returns = 0;
    SpaceWaster first(1000, 0);

    for (int i = 0; i < repeats; ++i) {
        #ifdef THROW
            try {
                thrower(&first);
            } catch (std::exception &e) {
                ++returns;
            }
        #else
            returner(&first);
            ++returns;
        #endif
    }
    #ifdef THROW
        std::cout << returns << " exceptions\n";
    #else
        std::cout << returns << " returns\n";
    #endif
}

Результаты бенчмаркинга Микки Мауса:

$ make throw -B && time ./throw
g++     throw.cpp   -o throw
1001 returns

real    0m0.547s
user    0m0.421s
sys     0m0.046s

$ make throw CPPFLAGS=-DTHROW -B && time ./throw
g++  -DTHROW   throw.cpp   -o throw
1001 exceptions

real    0m2.047s
user    0m1.905s
sys     0m0.030s

Таким образом, в этом случае создание исключения до 1000 уровней стека вместо обычного возврата занимает около 1,5 мс. Это включает в себя ввод блока try, который, как я полагаю, в некоторых системах является бесплатным во время выполнения, в других - затраты каждый раз, когда вы вводите try, а в других - только при каждом входе в функцию, содержащую попытку. Для более вероятного 100 уровней стека я увеличил количество повторений до 10 000, потому что все было в 10 раз быстрее. Таким образом, исключение составляет 0,1 мс.

Для 10 000 уровней стека это было 18,7 с против 4,1 с, поэтому около 14 мс дополнительных затрат для исключения. Так что в этом примере мы рассматриваем довольно постоянные издержки в 1,5us на уровень стека (где каждый уровень разрушает один объект).

Очевидно, что C ++ 0x не определяет производительность для исключений (или чего-либо еще, кроме сложности Big-O для алгоритмов и структур данных). Я не думаю, что это изменяет исключения таким образом, что это серьезно повлияет на многие реализации, положительно или отрицательно.

14 голосов
/ 19 июня 2009

Исключительная производительность очень зависит от компилятора. Вы должны будете профилировать свою заявку, чтобы увидеть, если это проблема. В общем, не должно быть.

Вы действительно должны использовать исключения для «исключительных условий», а не для общей логической обработки. Исключения идеально подходят для разделения нормальных путей через ваш код и пути ошибок.

10 голосов
/ 20 июня 2009

Я в основном думаю, что был задан неправильный вопрос.
Какова стоимость исключения не является полезной, более полезной является стоимость исключений относительно альтернативы. Таким образом, вам нужно измерить, сколько стоит исключение, и сравнить это с возвращением кодов ошибок >>> И <<<, проверяющих коды ошибок на каждом уровне стека, раскручивают. </p>

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

По сути, его следует использовать для передачи управления на более высокий уровень контекста, где объект с достаточным контекстом поймет, как обрабатывать исключительную ситуацию.

Учитывая этот принцип использования, мы видим, что исключения будут использоваться для передачи управления на несколько уровней в кадре стека. Теперь рассмотрим дополнительный код, который нужно написать, чтобы передать код ошибки в тот же стек вызовов. Подумайте о дополнительной сложности, которая добавляется тогда, когда коды ошибок могут поступать из разных направлений, и попытайтесь объединить все типы кодов ошибок.

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

Ответ, как всегда, зависит (заполняйте профиль и используйте быстрый, если вам это нужно).

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

8 голосов
/ 19 июня 2009

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

try{
    CheckInterrupts();
    *(uint32_t*)&op_cache=ReadDword(cCS,eip);
    (this->*Opcodes[op_cache[0]])();
    //operate on the this class with the opcode functions in this class
    eip=(uint16_t)eip+1;

}
//eventually, handle these and do CpuInts...
catch(CpuInt_excp err){
    err.code&=0x00FF;
    switch(err.code){

Из-за накладных расходов, связанных с размещением этого кода в блоке try, функции исключений стали двумя из 5 лучших пользователей процессорного времени.

То, мне дорого

6 голосов
/ 19 июня 2009

Нет причин, по которым исключения в C ++ 0x должны быть быстрее или медленнее, чем в C ++ 03. А это значит, что их производительность полностью зависит от реализации. Windows использует совершенно разные структуры данных для реализации обработки исключений в 32-разрядной и 64-разрядной версиях, а также в Itanium и x86. Linux не гарантированно придерживается одной и только одной реализации. Это зависит. Существует несколько популярных способов реализации обработки исключений, и все они имеют свои преимущества и недостатки.

Так что это зависит не от языка (c ++ 03 против 0x), а от компилятора, библиотеки времени выполнения, архитектуры ОС и процессора.

4 голосов
/ 19 июня 2009

Я не думаю, что C ++ 0x добавляет или изменяет что-либо в том, как работают исключения C ++. Для общего совета посмотрите здесь .

3 голосов
/ 19 июня 2009

Можно было бы предположить, что они будут иметь примерно такую ​​же производительность, как в C ++ 03, что "очень медленно!" И нет, исключения должны использоваться в исключительных случаях из-за конструкции try-catch-throw на любом языке. Вы делаете что-то не так, если вы используете throw для управления потоком программ в Java.

0 голосов
/ 19 июня 2009

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

Это цена исключения.Если это предотвращает потерю данных или возгорание машины, это стоит затрат.В противном случае это, вероятно, не так.

РЕДАКТИРОВАТЬ: И так как это получило понижающее голосование (плюс повышенное голосование, так что +8 для меня!), Я поясню выше с меньшим количеством юмора и большей информацией: исключение,по крайней мере на земле C ++, требуются RTTI, компилятор и, возможно, магия операционной системы, что делает их производительность гигантской черной дырой неопределенности.(Вам даже не гарантируется, что они сработают, но такие случаи происходят во время других более серьезных событий, таких как нехватка памяти или просто уничтожение пользователем процесса или фактически загорающаяся машина.) Так что, если вы используете их, то этоЭто должно быть потому, что вы хотите изящно восстановиться в ситуации, которая в противном случае могла бы вызвать что-то ужасное, но у этого восстановления не может быть никаких ожиданий работоспособности (независимо от того, что это может быть для вашего конкретного приложения).

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

0 голосов
/ 19 июня 2009

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

Насколько я понял из некоторых чтений, например, для Visual C ++, есть некоторые довольно сложные структуры и логика, встроенная в код для обеспечения этого. Поскольку большинство функций могут вызывать другие функции, которые могут генерировать исключения, некоторые издержки для разматывания стека могут существовать даже в этих случаях.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...