Неожиданно хорошая производительность с параллелью openmp для цикла
Я отредактировал свой вопрос после предыдущих комментариев (особенно @Zboson) для лучшей читаемости
Я всегда действовал и следовал общепринятому мнению, что число потоков openmp должно примерно соответствовать числу гиперпотоков на машине для оптимальной производительности. Тем не менее, я наблюдаю странное поведение на моем новом ноутбуке с Intel Core i7 4960HQ, 4 ядра - 8 потоков. (УвидетьIntel документы здесь)
Вот мой тестовый код:
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <omp.h>
int main() {
const int n = 256*8192*100;
double *A, *B;
posix_memalign((void**)&A, 64, n*sizeof(double));
posix_memalign((void**)&B, 64, n*sizeof(double));
for (int i = 0; i < n; ++i) {
A[i] = 0.1;
B[i] = 0.0;
}
double start = omp_get_wtime();
#pragma omp parallel for
for (int i = 0; i < n; ++i) {
B[i] = exp(A[i]) + sin(B[i]);
}
double end = omp_get_wtime();
double sum = 0.0;
for (int i = 0; i < n; ++i) {
sum += B[i];
}
printf("%g %g\n", end - start, sum);
return 0;
}
Когда я скомпилирую его, используяgcc 4.9-4.9-20140209
с помощью команды:gcc -Ofast -march=native -std=c99 -fopenmp -Wa,-q
Я вижу следующее исполнение при измененииOMP_NUM_THREADS
[точки - это в среднем 5 прогонов, полосы ошибок (которые едва видны) - это стандартные отклонения]:
График является более четким, когда отображается как ускорение относительно OMP_NUM_THREADS = 1:
Производительность более или менее монотонно возрастает с увеличением количества потоков, даже если количество потоков omp очень сильно превышает ядро, а также число гипер-потоков! Обычно производительность должна падать, когда используется слишком много потоков (по крайней мере, в моем предыдущем опыте) из-за накладных расходов. Тем более, что вычисление должно быть связано с процессором (или, по крайней мере, с памятью) и не ожидать ввода-вывода.
Еще более странно, что ускорение в 35 раз!
Кто-нибудь может объяснить это?
Я также проверил это с гораздо меньшими массивами 8192 * 4 и увидел аналогичное масштабирование производительности.
В случае, если это имеет значение, я нахожусь на Mac OS 10.9 и данные о производительности были получены при запуске (под Bash):
for i in {1..128}; do
for k in {1..5}; do
export OMP_NUM_THREADS=$i;
echo -ne $i $k "";
./a.out;
done;
done > out
РЕДАКТИРОВАТЬ: Из любопытства я решил попробовать гораздо большее количество потоков. Моя операционная система ограничивает это до 2000. Странные результаты (как ускорение, так и низкие издержки потока) говорят сами за себя!
РЕДАКТИРОВАТЬ: Я попробовал последнее предложение @Zboson в их ответе, то есть поместил VZEROUPPER перед каждой математической функцией в цикле, и это решило проблему масштабирования! (Он также отправил однопоточный код от 22 с до 2 с!):