Почему станд.:: вектор:: оператор [] в 5 - 10 раз быстрее, чем станд.:: вектор:: в ()?

Во время оптимизации программы, пытаясь оптимизировать цикл, который выполняет итерации через вектор, я нашел следующий факт::: станд.:: вектор:: в () ЧРЕЗВЫЧАЙНО медленнее, чем оператор []!

Оператор [] в 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", таким образом, компилятор не "вытрет" его.

39
задан m0meni 28 May 2017 в 04:42
поделиться

3 ответа

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

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

57
ответ дан 27 November 2019 в 02:21
поделиться

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

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

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

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

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

29
ответ дан 27 November 2019 в 02:21
поделиться

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

4
ответ дан 27 November 2019 в 02:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: