Во время оптимизации программы, пытаясь оптимизировать цикл, который выполняет итерации через вектор, я нашел следующий факт::: станд.:: вектор:: в () ЧРЕЗВЫЧАЙНО медленнее, чем оператор []!
Оператор [] в 5 - 10 раз быстрее, чем в (), оба в выпуске и отладке создают (VS2008 x86).
Чтение немного в сети заставило меня понимать, что в () имеет граничную проверку. Хорошо, но, замедляя операцию максимум к 10 разам?!
Есть ли какая-либо причина этого? Я имею в виду, граничная проверка является простым сравнением числа, или я пропускаю что-то?
Вопрос - то, что настоящая причина для этого хита производительности?
Далее больше там какой-либо путь состоит в том, чтобы сделать его еще быстрее?
Я, конечно, собираюсь подкачать все мой в () вызовы с [] в других частях кода (в котором у меня уже есть пользовательская граничная проверка!).
Подтверждение концепции:
#define _WIN32_WINNT 0x0400
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <conio.h>
#include <vector>
#define ELEMENTS_IN_VECTOR 1000000
int main()
{
__int64 freq, start, end, diff_Result;
if(!::QueryPerformanceFrequency((LARGE_INTEGER*)&freq))
throw "Not supported!";
freq /= 1000000; // microseconds!
::std::vector<int> vec;
vec.reserve(ELEMENTS_IN_VECTOR);
for(int i = 0; i < ELEMENTS_IN_VECTOR; i++)
vec.push_back(i);
int xyz = 0;
printf("Press any key to start!");
_getch();
printf(" Running speed test..\n");
{ // at()
::QueryPerformanceCounter((LARGE_INTEGER*)&start);
for(int i = 0; i < ELEMENTS_IN_VECTOR; i++)
xyz += vec.at(i);
::QueryPerformanceCounter((LARGE_INTEGER*)&end);
diff_Result = (end - start) / freq;
}
printf("Result\t\t: %u\n\n", diff_Result);
printf("Press any key to start!");
_getch();
printf(" Running speed test..\n");
{ // operator []
::QueryPerformanceCounter((LARGE_INTEGER*)&start);
for(int i = 0; i < ELEMENTS_IN_VECTOR; i++)
xyz -= vec[i];
::QueryPerformanceCounter((LARGE_INTEGER*)&end);
diff_Result = (end - start) / freq;
}
printf("Result\t\t: %u\n", diff_Result);
_getch();
return xyz;
}
Править:
Теперь значение является assiged к "xyz", таким образом, компилятор не "вытрет" его.
Причина в том, что неконтролируемый доступ, вероятно, может быть выполнен с помощью одной инструкции процессора. Проверенный доступ также должен будет загрузить размер из памяти, сравнить его с индексом и (при условии, что он находится в диапазоне) пропустить условную ветвь к обработчику ошибок. Возможно, потребуется больше усилий, чтобы обработать возможность выброса исключения. Это будет во много раз медленнее, и именно поэтому у вас есть оба варианта.
Если вы можете доказать, что индекс находится в пределах диапазона без проверки во время выполнения, используйте оператор []
. В противном случае используйте at ()
или добавьте свой собственный чек перед доступом. operator []
должен работать как можно более или менее быстро, но будет беспорядочно взорваться, если индекс недействителен.
Я запустил ваш тестовый код на своей машине:
В неоптимизированной отладочной сборке разница между двумя циклами незначительна.
В оптимизированной сборке выпуска второй цикл for полностью оптимизирован (вызов operator []
, вероятно, встроен, и оптимизатор видит, что цикл ничего не делает, и может удалить весь цикл) .
Если я изменю тело циклов для выполнения какой-то реальной работы, например, vec.at (i) ++;
и vec [i] ++;
соответственно , разница между двумя петлями незначительна.
Я не вижу той разницы в производительности в пять-десять раз, которую вы видите.
Вы ничего не делаете с возвращаемым значением, поэтому, если компилятор встраивает эти функции, он может полностью их оптимизировать. Или, возможно, он может полностью оптимизировать версию с индексом ([]
). Запуск без оптимизации бесполезен с точки зрения измерения производительности, вам нужна простая, но полезная программа для выполнения функций, чтобы их не просто оптимизировать. Например, вы можете перетасовать вектор (случайным образом поменять местами 50000 пар элементов).