Цикл вокруг GPULaunch или в ядре с библиотекой ALEA
Мне нужно запустить ядро графического процессора (библиотека ALEA) 100 раз с одними и теми же данными, используя одно целое число (0-99) в качестве параметра. Я попытался реализовать этот цикл в ядре, но получил странные результаты. Я должен был взять цикл из ядра и вокруг функции GPULaunch, как это:
var lp = new LaunchParam(GridDim, BlockDim);
for (int i= 0; i < 100; i++)
{
GPULaunch(TestKernel, lp, Data, i);
}
Версия кода для процессора высоко оптимизирована и эффективно использует 4 ядра (%100). После реорганизации данных в памяти в соответствии с принципами объединенного доступа к памяти у меня может быть%92 Occupancy и%96 Global Load Efficiency. Но, тем не менее, версия GPU только на 50% быстрее, чем версия CPU. У меня есть сомнения, эффективен ли зацикливание GPULaunch таким образом.
Как вы видите на графике ниже, я не вижу повторной передачи памяти в NVIDIA Visual Profilier. После того, как я загрузил данные в графический процессор (не видно на графике, но для меня это не важно), я получаю одну короткую передачу памяти из 100 циклов, как видно на правом конце. Итак, мой вопрос:
- Имеет ли этот метод вызова GPULaunch в цикле невидимую передачу в память тех же данных?
- Если есть такие издержки, мне нужно иметь этот цикл в ядре. Как мне это сделать. Я пытался, но получил нестабильные результаты, думая, что этот метод не вписывается в архитектуру параллельного программирования на GPU.
заранее спасибо
1 ответ
Я попытался реализовать цикл в ядре еще раз, и это сработало. Я не уверен, что отличается в этот раз. Вот очевидный код (просто шаблон, а не рабочий код):
public class GPUModule : ILGPUModule
{
public GPUModule (GPUModuleTarget target) : base(target)
{
}
[Kernel]
Public MyKernel(deviceptr<int> Data)
{
var start = blockIdx.x * blockDim.x + threadIdx.x;
int ind = threadIdx.x;
for (int i=0;i<100;i++)
{
//Kernel Code here
}
}
public void Dilimle_VerilerB(deviceptr<int> Data
{
...
var lp = new LaunchParam(GridDim, BlockDim);
GPULaunch(TestKernel, lp, Data, HOIndex);
...
}
}
Единственным дополнением к ядру был цикл с целым числом "i". К сожалению, это привело к тому, что число регистров / потоков выросло с 26 до 42, что привело к снижению занятости с%100 до%50, что немного увеличило время выполнения с 2,1 до 2,3 с. Таким образом, если бы сохранялась занятость на уровне 100%, включение цикла в ядро увеличило бы производительность, а значит, значительно сократило издержки GPULaunch.
Занятость%100 с циклом вокруг GPULaunch работала с 1024 потоками / блок. После изменения внутреннего цикла ядра я изменил его на 128 потоков / блок. Это увеличило Ocuppancy до%62 и привело к времени выполнения 1,1 с. Таким образом, заключение такого цикла в ядро позволило увеличить производительность графического процессора в 2 раза.
Таким образом, вопрос в том, почему число регистров / потоков в ядре увеличивается с 26 до 42 с одним добавлением одного цикла целого числа к ядру. Я предполагаю, что Occupancy все еще может быть близким к%100, если число регистров будет около 30.