C++ 3d.Комментарии

7b6e9298

Использование конструкторов и деструкторов


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

Если вы решили, что тем самым должна повыситься производительность, ввиду того, что в теле функции отсутствуют блоки try/catch, то должен вас огорчить -- они будут автоматически сгенерированы компилятором для корректной обработки раскрутки стека. Но все-таки, какая версия выделения ресурсов обеспечивает большую производительность? Давайте протестируем следующий код: #include <stdio.h> #include <stdlib.h> #include <time.h>

void ResourceAcquire(); void ResourceRelease(); void Work();

struct RAII { RAII() { ResourceAcquire(); } ~RAII() { ResourceRelease(); } };

void f1() { ResourceAcquire();

try { Work(); } catch (...) { ResourceRelease(); throw; }

ResourceRelease(); }

void f2() { RAII raii; Work(); }

long Var, Count;

void ResourceAcquire() { Var++; } void ResourceRelease() { Var--; } void Work() { Var+=2; }

int main(int argc, char** argv) { if (argc>1) Count=atol(argv[1]);

clock_t c1, c2; { c1=clock();

for (long i=0; i<Count; i++) for (long j=0; j<1000000; j++) f1();

c2=clock(); printf("f1(): %ld mln calls per %.1f sec\n", Count, double(c2-c1)/CLK_TCK); } { c1=clock();

for (long i=0; i<Count; i++) for (long j=0; j<1000000; j++) f2();

c2=clock(); printf("f2(): %ld mln calls per %.1f sec\n", Count, double(c2-c1)/CLK_TCK); } }

Как выдумаете, какая функция работает быстрее? А вот и нет! В зависимости от компилятора быстрее работает то f1(), то f2(), а иногда они работают совершенно одинаково из-за полной идентичности сгенерированного компилятором кода. Все зависит от используемых принципов обработки исключений и качества оптимизатора.

Как же работают исключения? Если вкратце, то в разных реализациях исключения работают по-разному. И всегда чрезвычайно нетривиально! Особенно много сложностей возникает с ОС, использующими так называемый Structured Exception Handling и/или поддерживающими многопоточность (multithreading). Фактически, с привычными нам современными ОС...


На текущий момент в Internet можно найти достаточное количество материала по реализации exception handling (EH) в C++ и не только, приводить здесь который не имеет особого смысла. Тем не менее, влияние EH на производительность C++ программ заслуживает отдельного обсуждения.

Увы, но стараниями недобросовестных "преувеличителей достоинств" в массы пошел миф о том, что обработку исключений можно реализовать вообще без накладных расходов. На самом деле это не так, т.к. даже самый совершенный метод реализации EH, отслеживающий созданные (и, следовательно, подлежащие уничтожению) на данный момент (под)объекты по значению счетчика команд (например, регистр (E)IP процессоров Intel-архитектуры) не срабатывает в случае создания массивов.

Но более надежным (и, кстати, не зависящим от способа реализации EH) опровержением исходной посылки является тот факт, что EH добавляет дополнительные дуги в Control Flow Graph, т.е. в граф потоков управления, что не может не сказаться на возможностях оптимизаци.

Тем не менее, накладные расходы на EH в лучших реализациях не превышают 5%, что с практической точки зрения почти эквивалентно полному отсутствию расходов.

Но это в лучших реализациях! О том, что происходит в реализациях "обычных" лучше не упоминать -- как говорит герой известного анекдота: "Гадкое зрелище"...


Содержание раздела