Моя программа Cuda получает значительное повышение производительности (в среднем) в зависимости от размера блоков & # блоков; где общее количество «нитей» остается неизменным. (Я не уверен, что поток является правильной терминологией ... но я буду использовать его здесь, где для каждого ядра общее количество потоков (# блоков) * (размер блока)). Я сделал несколько графиков, чтобы проиллюстрировать мою мысль.GPGPU: влияние размера блока на производительность программы, почему моя программа работает быстрее при очень определенных размерах?
Но сначала позвольте мне объяснить, что мой алгоритм первый, , однако я не уверен, насколько он уместен, потому что я бы предположил, что это то, что относится ко всем программам GPGPU. Но, возможно, я ошибаюсь в этом ,
В основном я просматриваю большие массивы, которые логически рассматриваются как 2D-массивы, где каждый поток добавляет элемент из массива, а также добавляет квадрат этого значения к другой переменной, а затем в конце записывает значение в другой массив , где во время каждого считывания все потоки сдвигаются определенным образом. Вот мой код ядра:
__global__ void MoveoutAndStackCuda(const float* __restrict__ prestackTraces, float* __restrict__ stackTracesOut,
float* __restrict__ powerTracesOut, const int* __restrict__ sampleShift,
const unsigned int samplesPerT, const unsigned int readIns,
const unsigned int readWidth, const unsigned int defaultOffset) {
unsigned int globalId = ((blockIdx.x * blockDim.x) + threadIdx.x); // Global ID of this thread, starting from 0 to total # of threads
unsigned int jobNum = (globalId/readWidth); // Which array within the overall program this thread works on
unsigned int readIndex = (globalId % readWidth) + defaultOffset; // Which sample within the array this thread works on
globalId = (jobNum * samplesPerT) + readIndex; // Incorperate default offset (since default offset will also be the offset of
// index we will be writing to), actual globalID only needed for above two variables.
float stackF = 0.0;
float powerF = 0.0;
for (unsigned int x = 0; x < readIns; x++) {
unsigned int indexRead = x + (jobNum * readIns);
float value = prestackTraces[readIndex + (x * samplesPerT) + sampleShift[indexRead]];
stackF += value;
powerF += (value * value);
}
stackTracesOut[globalId] = stackF;
powerTracesOut[globalId] = powerF;
}
Теперь для мяса этого поста, при вызове этого кода
MoveoutAndStackCuda<<<threadGroups, threadsPerGroup>>>(*prestackTracesCudaPtr,
*stackTracesOutCudaPtr, *powerTracesOutCudaPtr,
*sampleShiftCudaPtr, samplesPerT, readIns,
readWidth, defaultOffset);
Все, что я сделал, отличаются threadGroups и threadsPerGroup внутри < < < >>>, где threadGroups.x * threadsPerGroup.x остается неизменным. (Как указывалось ранее, это одномерная задача).
Я увеличил размер блока на 64, пока не достиг 1024. Я не ожидал изменений, потому что я понял, что размер блока больше 32, что, по моему мнению, является количеством ALU в ядре, оно будет работать как как можно быстрее. Посмотрите на этом графике я сделал:
Для этого конкретного размера общего числа потоков является 5000 * 5120, так, например, если размер блока равен 64, то есть ((5000 * 5120)/64). По какой-то причине существует значительный прирост производительности при размере блока 896, 768 и 512. Почему?
Я знаю, что это выглядит случайным образом, но каждая точка на этом графике - это 50 тестов, усредненных вместе!
Вот еще один график, на этот раз, когда общее количество потоков будет (8000 * 8192). На этот раз бустер на 768 и 960.
Еще один пример, на этот раз для работы, которая меньше, чем у двух других проблем (всего резьб 2000 * 2048):
Фактически вот альбом, который я сделал из этих графиков, с каждым графиком, представляющим другой размер проблемы: graph album.
Я запускаю этот a Quadro M5000, который имеет 2048 Cuda Cores. Я считаю, что у каждого Cuda Core есть 32 ALU, поэтому я полагаю, что общее количество вычислений, которое может происходить в любой момент времени (2048 * 32)?
Итак, что объясняет эти магические числа?Я полагал, что это может быть общее количество потоков, разделенных # ядрами cuda, или разделенных на (2048 * 32), но до сих пор я не нашел никакой корреляции ни с чем, что простирается на все графики в моем альбоме. Есть ли еще одно испытание, которое я мог бы сделать, чтобы помочь сузить дело? Я хочу узнать, какой размер блока для запуска этой программы за наилучшие результаты.
Также я не включил его, но я также проверил, где размер блока уменьшился на 1 из 32, а вещи стали экспоненциально медленнее. Это имеет смысл для меня с тех пор, у нас меньше локальных потоков на группу, чем ALU в заданном многопроцессоре.
Я не проанализировал ваш вопрос подробно, но я бы посоветовал вам использовать инструменты профилирования CUDA (например, NVIDIA Visual Profiler), так как они неплохие. Они могут точно сказать, какая часть вашей программы медленнее/быстрее для различных рассмотренных вами случаев. –
http://stackoverflow.com/q/9985912/681865 – talonmies