premature optimization?? или это мои косяки

Тема в разделе "LANGS.C", создана пользователем varnie, 30 авг 2008.

  1. varnie

    varnie New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2005
    Сообщения:
    1.785
    привет всем!

    сегодня сидел рефакторил свой проект и параллельно отслеживал его время исполнения.
    ну объясните мне, почему такие логичные вещи как нижеследующие ведут себя совсем не так как того требует логика???
    ниже идет список, где первый вариант -- это бывший, второй -- мой новый. хоть убейте, я не понимаю, почему ВЕЗДЕ вариант *a работает быстрее чем усовершенствованный вариант *b.

    итак...
    1a)
    Код (Text):
    1. CFunctionsPtrVector::const_iterator iter =
    2.         std::find_if(g_UsersFunctions.begin(), g_UsersFunctions.end(), CFuncNamesEqualFunctor("main"));
    3.     if (iter != g_UsersFunctions.end()) {
    4.         (*iter)->invoke( CFunction::ArgsList()  );
    5.     } else {
    6.         throw CRunTimeException("main function not declared", NULL);
    7.     }
    1b)
    Код (Text):
    1. CFunctionsPtrVector::const_iterator iter =
    2.         std::find_if(g_UsersFunctions.begin(), g_UsersFunctions.end(), CFuncNamesEqualFunctor("main"));
    3.     if (iter == g_UsersFunctions.end()) {
    4.          throw CRunTimeException("main function not declared", NULL);
    5.     }
    6.    
    7.      (*iter)->invoke( CFunction::ArgsList()  );
    8.     }
    2a)
    Код (Text):
    1. for (CFunctionsPtrVector::const_iterator iter = g_UsersFunctions.begin(); iter != g_UsersFunctions.end(); ++iter) {
    2.  ...
    3. }
    2b)
    Код (Text):
    1. for (CFunctionsPtrVector::const_iterator iter = g_UsersFunctions.begin(), iter_end = g_UsersFunctions.end(); iter != iter_end; ++iter) {
    2.  ...
    3. }
    цитата с буста:
    3a)
    Код (Text):
    1.  typedef boost::shared_ptr<Foo> FooPtr;
    2.  std::vector< FooPtr, boost::fast_pool_allocator<FooPtr> > vec;
    3.  //ниже используем индексную адресацию для доступа к элементам из vec
    3b)
    Код (Text):
    1.  typedef boost::shared_ptr<Foo> FooPtr;
    2.  std::vector< FooPtr, boost::pool_allocator<FooPtr> > vec;
    3.  //ниже используем индексную адресацию для доступа к элементам из vec
    4
    работа с std::stack с сохранением указателя на текущую его вершину для того чтобы по сто раз при кажд обращении к стэку не запрашивать его вершину через std::stack::top работает медленнее нежели тупое std::stack::top()->doSmth(); не смотря на то что здесь мы каждый раз обращаемся к std::stack::top()
    5a)
    Код (Text):
    1. while(condition){
    2.   std:vector<Foo> vec;
    3.   //работаем с vec
    4.   ...
    5. }
    5b)
    Код (Text):
    1. std::vector<Foo> vec;
    2. while(condition){
    3.   //работаем с vec
    4.   ...
    5.   vec.resize(0);
    6. }
    компилер gcc
    gcc -v
     
  2. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    varnie
    Насчёт варианта 1.
    Для if (condition) компилятор генерирует код, оптимизированный для случая condition==true.
    Так, для if (v1 == v2) будет сгенерирована инструкция jne, что при прочих равных условиях (начальном состоянии предиктора) приводит к более эффективному исполнению в случае равенства v1 и v2.
    Для явного указания шансов, что условие истинно, можно (в случае gcc) использовать __builtin_expect.

    Насчёт остальных - возможно какие-то оптимизации специально под RTL. Посмотри в асм-листинге, что там gcc нагенерировал.
     
  3. W4FhLF

    W4FhLF New Member

    Публикаций:
    0
    Регистрация:
    3 дек 2006
    Сообщения:
    1.050
    Предположительно в примере 2b идёт присвоение iter_end на каждой иттерации. И, по-скольку, эта переменная хранится в стеке это в итоге выходит медленнее, чем получение end() в регистр и его сравнение с iter, который, скорее всего, тоже хранится в регистре(2a).

    В 3 примере скорее всего зависимость от размера объекта. Надо читать доку boost'a, если там никаких оговорок не делается, то юзать просто то, что подходит больше(по скорости или функционалу).

    В 5 очевидно происходят какие-то ненужные манипуляции с памятью. Вместо vec.resize(0) я бы использовал vec.clear(). Так даже правильнее.
     
  4. varnie

    varnie New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2005
    Сообщения:
    1.785
    W4FhLF

    пример 2b: интересно то, что в куче профессионального С++ кода используется именно такой подход. и почему происходит присвоение iter_end на кажд. итерации?? как раз таки по-идее должно происходить только один раз! ведь на это и сделана "ставка".

    про пример 3 ничего конкретного сказать не могу.

    про пример 5:
    (c) http://en.wikibooks.org/wiki/Optimizing_C%2B%2B/Print_Version
    как раз мой случай.
     
  5. W4FhLF

    W4FhLF New Member

    Публикаций:
    0
    Регистрация:
    3 дек 2006
    Сообщения:
    1.050
    varnie

    Насчёт 2 вообще сложно рассуждать. Попробуй такой вариант:

    Код (Text):
    1. CFunctionsPtrVector::const_iterator iter = g_UsersFunctions.begin(), iter_end = g_UsersFunctions.end();
    2. for (; iter != iter_end; ++iter) {
    3.  ...
    4. }
    Кстати, с оптимизацией компилишь?

    Насчёт 5го больше ничего сказать не могу, без более полного примера. Т.е. с clear() медленнее или ты не пробовал?
     
  6. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    W4FhLF
    Вы считаете, что в gcc баг? Присвоение ведь находится в инициализационной части цикла.

    Мне кажется, что в 2а компилятор заинлайнил end(), опустив создание промежуточного итератора, и тем самым упростил сравнение. Тогда как в 2b такой промежуточный итератор форсируется.

    Лучше не гадать, а посмотреть асм-листинг gcc.
     
  7. varnie

    varnie New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2005
    Сообщения:
    1.785
    green
    угу, я про то же.
    W4FhLF
    твой вариант не сыграл роли.
    да, компилирую врубив оптимизацию.

    ps: я прекрасно понимаю что это микро-оптимизации, и от них толку как от ежа, но просто хочется разобраться, почему они ведут себя алогично.
     
  8. W4FhLF

    W4FhLF New Member

    Публикаций:
    0
    Регистрация:
    3 дек 2006
    Сообщения:
    1.050
    green, ну да, ты прав.

    Сравнивать асм-листинги надо тогда, что ещё остаётся?
     
  9. J0E

    J0E New Member

    Публикаций:
    0
    Регистрация:
    28 июл 2008
    Сообщения:
    621
    Адрес:
    Panama
    Скачай стандарт, это позволит тебе иметь не чужее, а верное мнение:
    "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 не должны иметь разницы даже в асм коде :\