Цикл вокруг 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 циклов, как видно на правом конце. Итак, мой вопрос:

  1. Имеет ли этот метод вызова GPULaunch в цикле невидимую передачу в память тех же данных?
  2. Если есть такие издержки, мне нужно иметь этот цикл в ядре. Как мне это сделать. Я пытался, но получил нестабильные результаты, думая, что этот метод не вписывается в архитектуру параллельного программирования на GPU.

заранее спасибо

NVIDIA Visual Profiler resutl

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.

Другие вопросы по тегам