C ++ 11 for_each и лямбда-оптимизация - PullRequest
4 голосов
/ 25 октября 2011

Я тестирую следующий код:

#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>
int main(int argc, char* argv[])
{
   std::vector<int> v(10000000);
   clock_t then = clock();
   if(argc <= 1)
      std::for_each(v.begin(), v.end(), [](int& it){ it = 10098; });
   else
      for(auto it = v.begin(); it != v.end(); ++it) *it = 98775;
   std::cout << clock() - then << "\n";
   return 0;
}

Я компилирую его с помощью g ++ 4.6, без каких-либо флагов оптимизации, и вот что я получаю:

[javadyan@myhost experiments]$ ./a.out 
260000
[javadyan@myhost experiments]$ ./a.out aaa
330000
[javadyan@myhost experiments]$ 

ИспользованиеОптимизация -O1 дает следующие (неудивительные) результаты:

[javadyan@myhost experiments]$ ./a.out 
20000
[javadyan@myhost experiments]$ ./a.out aaa
20000

Я использую Linux 3.0 на двухъядерном ноутбуке с частотой 2 ГГц, если это имеет значение.

Что мне интересно, так это какпрограмма, скомпилированная без какой-либо оптимизации, вызов for_each с лямбда-функцией может потреблять меньше часов, чем обычный цикл for?Не должно ли быть даже незначительных накладных расходов при вызове анонимной функции?Есть ли документация о том, как g ++ обрабатывает подобный код

 std::for_each(v.begin(), v.end(), [](int& it){ it = 10098; });

?Каково поведение других популярных компиляторов в этом случае?

ОБНОВЛЕНИЕ

Я не учел тот факт, что it во втором выражении сравнивается с v.end() на каждую итерацию.После исправления цикл for потребляет меньше часов, чем for_each.Однако мне все еще интересно, как компилятор оптимизирует for_each при использовании флага -O1.

Ответы [ 2 ]

7 голосов
/ 25 октября 2011

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

  for(auto it = v.begin(), end = v.end(); it != end; ++it) *it = 98775;

Кроме того, поскольку точный тип лямбды передается в for_each, есть очень хорошие шансы, что компилятор встроит его, в результате чего код не будет отличаться от цикла for. Обратите внимание, что в анонимных функциях нет виртуальных вызовов. Компилятор сделает что-то вроде этого:

class __lambda_function_123
{
public:
    void operator()(int& it) const{ it = 10098; }
};

std::for_each(v.begin(), v.end(), __lambda_function_123());

, который, помимо вставки, приведет к тому же коду, что и цикл for (с моей модификацией).

0 голосов
/ 25 октября 2011

Интересно, что даже после оптимизации "end = v.end (), i! = End" ваш случай for_each & lambda по-прежнему работает быстрее в Visual Studio 2010 в режиме отладки.

Одной из оптимизаций, которая делает его более быстрым в VS2010, является тот факт, что механизм мета-шаблонов может обнаруживать использование векторного итератора и переключаться на использование необработанных указателей на целые числа для начала / конца, что исключает множество проверок проверки во время режима отладки для обоих. оператор ++, оператор * и оператор! =.

...