привет всем! сегодня сидел рефакторил свой проект и параллельно отслеживал его время исполнения. ну объясните мне, почему такие логичные вещи как нижеследующие ведут себя совсем не так как того требует логика??? ниже идет список, где первый вариант -- это бывший, второй -- мой новый. хоть убейте, я не понимаю, почему ВЕЗДЕ вариант *a работает быстрее чем усовершенствованный вариант *b. итак... 1a) Код (Text): CFunctionsPtrVector::const_iterator iter = std::find_if(g_UsersFunctions.begin(), g_UsersFunctions.end(), CFuncNamesEqualFunctor("main")); if (iter != g_UsersFunctions.end()) { (*iter)->invoke( CFunction::ArgsList() ); } else { throw CRunTimeException("main function not declared", NULL); } 1b) Код (Text): CFunctionsPtrVector::const_iterator iter = std::find_if(g_UsersFunctions.begin(), g_UsersFunctions.end(), CFuncNamesEqualFunctor("main")); if (iter == g_UsersFunctions.end()) { throw CRunTimeException("main function not declared", NULL); } (*iter)->invoke( CFunction::ArgsList() ); } 2a) Код (Text): for (CFunctionsPtrVector::const_iterator iter = g_UsersFunctions.begin(); iter != g_UsersFunctions.end(); ++iter) { ... } 2b) Код (Text): for (CFunctionsPtrVector::const_iterator iter = g_UsersFunctions.begin(), iter_end = g_UsersFunctions.end(); iter != iter_end; ++iter) { ... } цитата с буста: 3a) Код (Text): typedef boost::shared_ptr<Foo> FooPtr; std::vector< FooPtr, boost::fast_pool_allocator<FooPtr> > vec; //ниже используем индексную адресацию для доступа к элементам из vec 3b) Код (Text): typedef boost::shared_ptr<Foo> FooPtr; std::vector< FooPtr, boost::pool_allocator<FooPtr> > vec; //ниже используем индексную адресацию для доступа к элементам из vec 4 работа с std::stack с сохранением указателя на текущую его вершину для того чтобы по сто раз при кажд обращении к стэку не запрашивать его вершину через std::stack::top работает медленнее нежели тупое std::stack::top()->doSmth(); не смотря на то что здесь мы каждый раз обращаемся к std::stack::top() 5a) Код (Text): while(condition){ std:vector<Foo> vec; //работаем с vec ... } 5b) Код (Text): std::vector<Foo> vec; while(condition){ //работаем с vec ... vec.resize(0); } компилер gcc gcc -v
varnie Насчёт варианта 1. Для if (condition) компилятор генерирует код, оптимизированный для случая condition==true. Так, для if (v1 == v2) будет сгенерирована инструкция jne, что при прочих равных условиях (начальном состоянии предиктора) приводит к более эффективному исполнению в случае равенства v1 и v2. Для явного указания шансов, что условие истинно, можно (в случае gcc) использовать __builtin_expect. Насчёт остальных - возможно какие-то оптимизации специально под RTL. Посмотри в асм-листинге, что там gcc нагенерировал.
Предположительно в примере 2b идёт присвоение iter_end на каждой иттерации. И, по-скольку, эта переменная хранится в стеке это в итоге выходит медленнее, чем получение end() в регистр и его сравнение с iter, который, скорее всего, тоже хранится в регистре(2a). В 3 примере скорее всего зависимость от размера объекта. Надо читать доку boost'a, если там никаких оговорок не делается, то юзать просто то, что подходит больше(по скорости или функционалу). В 5 очевидно происходят какие-то ненужные манипуляции с памятью. Вместо vec.resize(0) я бы использовал vec.clear(). Так даже правильнее.
W4FhLF пример 2b: интересно то, что в куче профессионального С++ кода используется именно такой подход. и почему происходит присвоение iter_end на кажд. итерации?? как раз таки по-идее должно происходить только один раз! ведь на это и сделана "ставка". про пример 3 ничего конкретного сказать не могу. про пример 5: (c) http://en.wikibooks.org/wiki/Optimizing_C%2B%2B/Print_Version как раз мой случай.
varnie Насчёт 2 вообще сложно рассуждать. Попробуй такой вариант: Код (Text): CFunctionsPtrVector::const_iterator iter = g_UsersFunctions.begin(), iter_end = g_UsersFunctions.end(); for (; iter != iter_end; ++iter) { ... } Кстати, с оптимизацией компилишь? Насчёт 5го больше ничего сказать не могу, без более полного примера. Т.е. с clear() медленнее или ты не пробовал?
W4FhLF Вы считаете, что в gcc баг? Присвоение ведь находится в инициализационной части цикла. Мне кажется, что в 2а компилятор заинлайнил end(), опустив создание промежуточного итератора, и тем самым упростил сравнение. Тогда как в 2b такой промежуточный итератор форсируется. Лучше не гадать, а посмотреть асм-листинг gcc.
green угу, я про то же. W4FhLF твой вариант не сыграл роли. да, компилирую врубив оптимизацию. ps: я прекрасно понимаю что это микро-оптимизации, и от них толку как от ежа, но просто хочется разобраться, почему они ведут себя алогично.
Скачай стандарт, это позволит тебе иметь не чужее, а верное мнение: "A directive that informs a vector of a planned change in size, so that it can manage the storage allocation accordingly. After reserve(), capacity() is greater or equal to the argument of reserve if reallocation happens; and equal to the previous value of capacity() otherwise. Reallocation happens at this point if and only if the current capacity is less than the argument of reserve()." Для очистки вектора можно делать swap, но это, скорее, снизит скорость. Интересно, а как компилилось и измерялось, раз стали видны такие мелочи? Случаи 2 и 4 не должны иметь разницы даже в асм коде :\