В настоящее время я изучаю CUDA, и мой алгоритм должен выполнять некоторые тяжелые вычисления на основе некоторых входных данных. Эти вычисления производятся в цикле, который вращается до 1024 раундов. Все работает нормально, пока у меня есть небольшое количество потоков (< 100'000) на каждое ядро, но если я хочу использовать больше потоков, ядро будет прервано окнами, так как требуется слишком много времени для завершения.Память CUDA (тип) для вычисления только устройства во время вызовов ядра (вычисление 1.1 или 1.2)
Мое решение было бы разделить тяжелые расчеты в нескольких вызовов ядра:
- главный ядро, которое подготавливает входные данные и вычисляет первые х раундов (петля раскатали). Это будет вызываться только один раз на каждый вход.
- работа Ядро, которое выполняет следующие x раундов (цикл развернут). Это будет вызываться так часто, как требуется для расчета всех необходимых раундов.
Между каждым вызов ядра (один главным, многие работы), я должен сохранить 16 + байты данных, которые будут использоваться при следующем вызове (длина длина ввода, он фиксируется на главный звонок). Основное ядро ядра начнет записывать эти байты, а ядро будет читать их, запускать следующие вычисления и записывать исходные данные с новым результатом. Мне нужны только эти данные на устройстве, не требуется хост-доступ. Какую память я должен использовать для этого? По крайней мере, это должна быть глобальная память, поскольку она является единственной записываемой памятью, которая сохраняется во время вызовов ядра, не так ли? Но тогда, что? Не могли бы вы дать мне совет о том, как мне приступить к работе с правильной памятью (и лучшая производительность)?
В «псевдокод», это может выглядеть следующим образом:
prepare memory to hold threads * (16 + length) bytes
for length = 1 to x step 1
call mainKernel
rounds = 1024 - rounds_done_in_main
for rounds to 0 step rounds_done_in_work
call workKernel
end for
end for
cleanup memory
--------
template <unsigned char length> __global__ mainKernel() {
unsigned char input[length];
unsigned char output[16];
const int tid = ...;
devPrepareInput<length>(input);
calc round 1: doSomething<length>(output, input)
calc round 2: doSomething<length>(output, output + input) // '+' == append
write data to memory based on tid // data == output + input
}
template <unsigned char length, remaining rounds> __global__ workKernel() {
unsigned char *input;
unsigned char *output;
const int tid = ...;
read data from memory based on tid
ouput = data
input = data+16
if rounds >= 1
calc round x : doSomething<length>(output, output + input)
if rounds >= 2
calc round x+1: doSomething<length>(output, output + input) // '+' == append
if rounds == x // x is the number of rounds in the last work call
do final steps on output
else
write ouput + input to memory based on tid (for next call)
}
Если у вас достаточно блоков, гораздо проще просто уменьшить размер сетки и запустить ядро несколько раз, добавив соответствующее смещение к номеру блока внутри ядра. – tera
Будет очень сложно сделать рекомендации о производительности, когда все, что вы можете предложить, - это псевдокод, содержащий множество шаблонных экземпляров «сделать что-то». Можете ли вы быть более конкретными в том, что вы действительно хотите знать? – talonmies
Код вообще не имеет значения, и это было бы слишком ошибкой для публикации здесь.Я спрашивал о том, что мне делать/какую память устройства я могу использовать для сохранения данных между вызовами ядра (строки с данными чтения/записи). Производительность была связана с этой памятью. – grubi