Для перебора элементов списка в C++ есть два основных способа - по индексам или итераторам. Для оптимизации процесса перебора обычно используется второй, т.к. компилятор в состоянии его хорошо заоптимизировать. Особняком в стандартных списках стоит вектор - по сути это обычный массив в обертке.
И вот недавно, просматривая очередное ревью, наткнулся на кусок кода, перебирающий std::vector по индексам. Этот код вызвал из глубин памяти некогда увиденную на просторах интернета информацию, что по индексам к вектору быстрее обращаться, чем по итераторам. Не доверяя памяти (а тем более интернету) решил проверить и вот какие результаты получились:
Iterator sum: 0, elapsed: 1060.44 (прогон по итераторам) Index sum: 0, elapsed: 950.36 (прогон по индексам) CIterator sum: 0, elapsed: 1055.48 (прогон по константным итераторам)
uint32_t sum = 0; LARGE_INTEGER frequency; LARGE_INTEGER t1, t2, elapsed;
QueryPerformanceFrequency(&frequency);
// By iterator QueryPerformanceCounter(&t1);
for (int i = 0; i < 1000000; ++i) for (std::vector::iterator it = a.begin(), e = a.end(); it != e; ++it) sum += it->VirtualAddress + it->PointerToRawData + it->SizeOfRawData;
// By const_iterator QueryPerformanceCounter(&t1);
for (int i = 0; i < 1000000; ++i) for (std::vector::iterator it = a.begin(), e = a.end(); it != e; ++it) sum += it->VirtualAddress + it->PointerToRawData + it->SizeOfRawData;
Несколько прогонов подряд дали один и тот же результат - по индексам на 10% (!) быстрее. Итераторы или константные итераторы в данном случае не роялют. Проверял на Visual Studio 2013, release x86 (в x64 такая же картина). В дизассемблер заглядывал - действительно немного разный код получается (точнее для индексов он выходит немного короче). Внутренность цикла - почти копи-паст из того ревью.
Понимая, что зависит от кода внутри цикла, компилятора и фаз луны, решил проверить еще и на маке. Вот результаты того же теста на Маке (Xcode 7.2, release):
for (int i = 0; i < 1000000; ++i) for (std::vector::iterator it = a.begin(), e = a.end(); it != e; ++it) sum += it->VirtualAddress + it->PointerToRawData + it->SizeOfRawData;
// By const_iterator start = mach_absolute_time();
for (int i = 0; i < 1000000; ++i) for (std::vector::iterator it = a.begin(), e = a.end(); it != e; ++it) sum += it->VirtualAddress + it->PointerToRawData + it->SizeOfRawData;
Понимая, что тест в какой-то степени синтетический (миллиард итераций в сумме получается), вывод тут напрашивается очень простой: не парьтесь, пишите кошерный код и будет вам скорость и счастье :)
JFYI: Использование в циклах auto из C++11 - это тоже самое, что и итераторы