Первое ядро ​​OpenMP намного медленнее второго ядра - PullRequest
0 голосов
/ 21 марта 2019

У меня инициализирован огромный двумерный массив 98306 на 98306.Я создал функцию ядра, которая подсчитывает общее количество элементов ниже определенного порога.

#pragma omp parallel for reduction(+:num_below_threshold)
for(row)
    for(col)
        index = get_corresponding_index(row, col);
        if (array[index] < threshold)
            num_below_threshold++;

Для целей тестирования я измерил время выполнения ядра, когда число потоков установлено на 1. Я заметилчто в первый раз ядро ​​запускается, это заняло около 11 секунд.Следующий вызов ядра, выполняющегося в том же массиве с одним потоком, занял всего около 3 секунд.Я думал, что это может быть проблема, связанная с кешем, но, похоже, это не связано.Каковы возможные причины этого?

Этот массив инициализируется как:

float *array = malloc(sizeof(float) * 98306 * 98306);
for (int i = 0; i < 98306 * 98306; i++) {
    array[i] = rand() % 10;
}

Это то же самое ядро ​​применяется к этому массиву дважды, и второе время выполнения намного быстрее, чем первоеядро.Я думаю о ленивом распределении в Linux, но это не должно быть проблемой из-за функции инициализации.Любые объяснения будут полезны.Спасибо!

1 Ответ

4 голосов
/ 21 марта 2019

Поскольку вы не предоставите Минимальный, полный и проверяемый пример , мне придется сделать некоторые дикие предположения, но я вполне уверен, что у меня есть суть проблемы.

Во-первых, вы должны заметить, что 98 306 x 98 306 составляет 9 664 069 636, что намного больше максимального значения, которое может хранить 32-разрядное целое число со знаком (то есть 2 147 483 647).Следовательно, верхний предел вашего цикла инициализации for после переполнения может стать 1,074,135,044 (как на моих машинах, хотя, строго говоря, это неопределенное поведение, может произойти все что угодно), что примерно в 9 раз меньше, чем вы ожидали.

Так что теперь, после цикла инициализации, только 11% памяти, которую вы считали выделенной, фактически были выделены и затронуты операционной системой.Тем не менее, ваш первый цикл сокращения хорошо справляется с просмотром различных элементов массива, и, поскольку примерно у 89% его, это первый раз, ОС тут же выполняет фактическое выделение памяти, что требует значительных затрат.количество времени.

И теперь, для вашего второго цикла сокращения, вся память была правильно выделена и затронута, что делает ее намного быстрее.

Так вот, как я полагаю, произошло.Тем не менее, здесь могут быть задействованы многие другие параметры, такие как:

  • Замена : массив, который вы пытаетесь выделить, представляет около 36 ГБ памяти.Если на вашем компьютере недостаточно памяти, ваш код может поменяться местами, что может привести к серьезным ошибкам при любом измерении производительности, которое вы можете получить с помощью
  • NUMA эффекта : еслина вашей машине есть несколько узлов NUMA, тогда закрепление потоков и привязка памяти, если не будут должным образом управляться, могут сильно повлиять на производительность между циклами
  • Оптимизация компилятора : вы не упомянули, какиеиспользуемый вами компилятор и какой уровень оптимизации вы запрашивали.В зависимости от этого, вы будете поражены тем, насколько коротким может стать ваш код.Например, компилятор может полностью удалить второй цикл, поскольку он делает то же самое, что и первый, и становится бесполезным, так как результат будет таким же ... И много других интересных и неожиданных вещей, которые делают ваш тест бессмысленным
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...