Почему std :: vector :: operator [] в 5-10 раз быстрее, чем std :: vector :: at ()? - PullRequest
39 голосов
/ 17 июля 2010

Во время оптимизации программы, пытаясь оптимизировать цикл, который повторяет вектор, я обнаружил следующий факт: :: std :: vector :: at () НАМНОГО медленнее, чем operator []!

Оператор [] работает в 5-10 раз быстрее, чем при () , как в сборках выпуска, так и отладки (VS2008 x86).

Чтение в Интернете дало мне понять, что at () имеет проверку границ. Хорошо, но, замедляя работу в 10 раз?!

Есть ли причина для этого? Я имею в виду, проверка границ - это простое сравнение чисел или я что-то упустил?
Вопрос в том, что является реальной причиной этого удара производительности?
Более того, есть ли способ сделать это еще быстрее ?

Я, конечно, собираюсь поменять все мои вызовы at () с [] в других частях кода (в которых у меня уже есть пользовательская проверка границ!).

Подтверждение концепции:

#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;
}

Edit:
Теперь значение присваивается «xyz», поэтому компилятор не будет «стирать» его.

Ответы [ 3 ]

57 голосов
/ 17 июля 2010

Причина в том, что доступ без контроля, вероятно, может быть выполнен с помощью одной инструкции процессора.Проверенный доступ также должен будет загрузить размер из памяти, сравнить его с индексом и (при условии, что он находится в диапазоне) пропустить условную ветвь к обработчику ошибок.Там может быть больше возни, чтобы справиться с возможностью создания исключения.Это будет во много раз медленнее, и именно поэтому у вас есть оба варианта.

Если вы можете доказать, что индекс находится в пределах диапазона без проверки во время выполнения, используйте operator[].В противном случае, используйте at() или добавьте свою собственную проверку перед доступом.operator[] должно быть более или менее быстрым, насколько это возможно, но может взорваться, если индекс недействителен.

29 голосов
/ 17 июля 2010

Я запустил ваш тестовый код на моей машине:

В неоптимизированной отладочной сборке разница между двумя циклами незначительна.

В оптимизированной сборке релиза второй цикл for полностью оптимизирован (вызов operator[], вероятно, встроен, и оптимизатор может видеть, что цикл ничего не делает и может удалить весь цикл).

Если я изменю тело циклов для выполнения какой-либо реальной работы, например, vec.at(i)++; и vec[i]++;, соответственно, разница между двумя циклами будет незначительной.

Я не вижу той разницы в производительности в пять-десять раз, которую вы видите.

4 голосов
/ 17 июля 2010

Вы ничего не делаете с возвращаемым значением, поэтому, если компилятор включает эти функции, он может полностью их оптимизировать.Или, возможно, он может полностью оптимизировать нижнюю версию ([]).Работа без оптимизации бесполезна с точки зрения измерения производительности, вам нужна простая, но полезная программа для реализации функций, чтобы их не просто оптимизировали.Например, вы можете перемешать вектор (случайным образом поменять местами 50000 пар элементов).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...