Приведение производительности от size_t к двойному
TL; DR: Почему умножение / приведение данных вsize_t
медленно и почему это зависит от платформы?
У меня проблемы с производительностью, которые я до конца не понимаю. Контекст представляет собой устройство захвата кадров камеры, где изображение uint16_t размером 128x128 считывается и обрабатывается с частотой несколько 100 Гц.
При постобработке я генерирую гистограммуframe->histo
который имеетuint32_t
и имеетthismaxval
= 2 ^ 16 элементов, в основном я подсчитываю все значения интенсивности. Используя эту гистограмму, я вычисляю сумму и квадратную сумму:
double sum=0, sumsquared=0;
size_t thismaxval = 1 << 16;
for(size_t i = 0; i < thismaxval; i++) {
sum += (double)i * frame->histo[i];
sumsquared += (double)(i * i) * frame->histo[i];
}
Профилируя код с профилем, я получил следующее (образцы, процент, код):
58228 32.1263 : sum += (double)i * frame->histo[i];
116760 64.4204 : sumsquared += (double)(i * i) * frame->histo[i];
или первая строка занимает 32% процессорного времени, вторая строка 64%.
Я провел некоторый бенчмаркинг, и мне кажется, что это тип данных / кастинг, который проблематичен. Когда я изменяю код на
uint_fast64_t isum=0, isumsquared=0;
for(uint_fast32_t i = 0; i < thismaxval; i++) {
isum += i * frame->histo[i];
isumsquared += (i * i) * frame->histo[i];
}
работает ~ в 10 раз быстрее. Тем не менее, это снижение производительности также зависит от платформы. На рабочей станции Core i7 CPU 950 @ 3,07 ГГц код работает в 10 раз быстрее. На моем Macbook8,1, который имеет Intel Core i7 Sandy Bridge 2,7 ГГц (2620M), код всего в 2 раза быстрее.
Теперь мне интересно:
Почему оригинальный код такой медленный и легко ускоряется? Почему это сильно зависит от платформы?Обновить
Я скомпилировал приведенный выше код с помощью
g++ -O3 -Wall cast_test.cc -o cast_test
Update2:
Я запускал оптимизированные коды через профилировщик Инструменты на Mac, какАкул) и нашел две вещи:
1) Сам цикл в некоторых случаях занимает значительное время.thismaxval
имеет типsize_t
.
for(size_t i = 0; i < thismaxval; i++)
занимает 17% моего общего времени выполненияfor(uint_fast32_t i = 0; i < thismaxval; i++)
берет 3,5%for(int i = 0; i < thismaxval; i++)
не отображается в профилировщике, я предполагаю, что это меньше, чем 0,1%2) Типы данных и данные о кастингах таковы:
sumsquared += (double)(i * i) * histo[i];
15% (сsize_t i
)sumsquared += (double)(i * i) * histo[i];
36% (сuint_fast32_t i
)isumsquared += (i * i) * histo[i];
13% (сuint_fast32_t i
, uint_fast64_t isumsquared
)isumsquared += (i * i) * histo[i];
11% (сint i
, uint_fast64_t isumsquared
)Удивительно,int
быстрее чемuint_fast32_t
?
Update4:
Я провел еще несколько тестов с разными типами данных и разными компиляторами на одной машине. Результаты приведены ниже
Для testd 0 - 2 соответствующий код
for(loop_t i = 0; i < thismaxval; i++)
sumsquared += (double)(i * i) * histo[i];
сsumsquared
двойной иloop_t
size_t
, uint_fast32_t
а такжеint
для тестов 0, 1 и 2.
Для тестов 3--5 код
for(loop_t i = 0; i < thismaxval; i++)
isumsquared += (i * i) * histo[i];
сisumsquared
типаuint_fast64_t
а такжеloop_t
опять такиsize_t
, uint_fast32_t
а такжеint
для тестов 3, 4 и 5.
Я использовал следующие компиляторы: gcc 4.2.1, gcc 4.4.7, gcc 4.6.3 и gcc 4.7.0. Временные параметры указаны в процентах от общего времени процессора, поэтому они показывают относительную производительность, а не абсолютную (хотя время выполнения было довольно постоянным в 21 с). Время процессора для обеих двух строк, потому что я не совсем уверен, правильно ли разделитель разделил две строки кода.
gcc: 4.2.1 4.4.7 4.6.3 4.7.0 ---------------------------------- test 0: 21.85 25.15 22.05 21.85 test 1: 21.9 25.05 22 22 test 2: 26.35 25.1 21.95 19.2 test 3: 7.15 8.35 18.55 19.95 test 4: 11.1 8.45 7.35 7.1 test 5: 7.1 7.8 6.9 7.05
или
Исходя из этого, кажется, что приведение стоит дорого, независимо от того, какой тип целого числа я использую.
Также кажется, что gcc 4.6 и 4.7 не могут должным образом оптимизировать цикл 3 (size_t и uint_fast64_t).