CUDA: могут ли устройство и код работать параллельно до истечения срока жизни, пинг-понг данных? - PullRequest
1 голос
/ 17 декабря 2011

Я программирую матричные умножения вектора с помощью Cuda. Матрица состоит из блоков, поэтому каждый поток может хранить один блок матрицы в общей памяти (как локальные переменные потока). Я также отправляю вектор в качестве аргумента, умножение выполняется в блоках, без проблем. Но проблема зависит от времени. Я должен вычислить матричное векторное произведение с той же самой матрицей, но разным вектором для каждого временного среза. Поэтому было бы напрасно вызывать ядро ​​для каждой оценки продукта, вызывая копирование блоков матрицы из глобальной памяти графической карты в общую память. Я думал, что смогу сделать один вызов ядра и поддерживать его всегда, не теряя локальные переменные потока. Вектор может быть выделен как отображенная память в хосте, скажем V. Теперь ядро ​​умножается, сохраняет его в еще одной отображенной области, скажем P, устанавливает флаг (еще одно целочисленное отображение памяти). Хост опрашивает флаг, как только флаг установлен, он отображает произведение из P, загружает новый вектор в V и сбрасывает флаг. Ядро также опрашивает флаг, видит сброс, умножает, сохраняет продукт в P и устанавливает флаг.

Я написал программу сложения поменьше, чтобы посмотреть, работает ли такая межпроцессная связь, но это не так. Проблема в том, что когда ядро ​​записывает что-либо в отображенную память, память на хосте не обновляется, пока хост не вызовет cudaThreadSynchronize (); Но если синхронизация потоков не может произойти, пока ядро ​​опрашивает флаг. Есть ли выход из этой ситуации?

Есть ли какой-нибудь другой некрасивый способ реализации такого межпроцессного взаимодействия. Конечно, в руководствах говорится, что отображенная память помогает амортизировать латентность, что позволяет на одно время копировать. Поскольку они не объяснили основной механизм, который налагает такое ограничение, я дал толчок этой идее.

Любая помощь приветствуется.

Спасибо,

Elan.

1 Ответ

1 голос
/ 17 декабря 2011

Я не уверен, полностью ли я понимаю вашу проблему, но да, у вас может быть несколько активных одновременно работающих ядер.

Из Руководства по вычислениям CUDA v. 3.2 pg.38: http://developer.download.nvidia.com/compute/cuda/3_2/toolkit/docs/CUDA_C_Programming_Guide.pdf

Некоторые устройства с вычислительной способностью 2.x могут выполнять несколько ядер одновременно.Приложения могут запрашивать эту возможность, вызывая cudaGetDeviceProperties () и проверяя свойство concurrentKernels.

Мой вопрос к вам - почему вы хотите использовать несколько ядер?

Я думаю, что вам нужноперечитайте руководство CUDA (см. выше).Похоже, что вы хотите сделать, это одно ядро ​​с несколькими блоками / потоками, каждый из которых имеет свой кусок общей памяти.Далее вам нужно выяснить, насколько большой элемент использовать для каждого блока.Помните, что вам нужны два «куска» памяти и продукт (3 квадратные 2D матрицы одинакового размера).Для этого сделайте запрос к устройству и получите вычислительную мощность и прочитайте приведенное выше руководство, чтобы определить итоговый объем разделяемой памяти.

Затем используйте код, подобный следующему:

if (ComputeCapability >= 2.0)
   {
      NumberOfSharedValues = (32768/GetSize(Dummy));
      FullUseageThreadsPerBlock = 512;
      MaxBlocksPerSM = 3;
   }
   else
   {
      //Tot. Shared mem / Size per var / Number of Arrays
      NumberOfSharedValues = (16384/GetSize(Dummy)/3);

      //CC1.2 && CC1.3
      if (ComputeCapability >= 1.2)
      {
         FullUseageThreadsPerBlock = 512;
         MaxBlocksPerSM = 2;
      }  
      else  //CC1.0 && CC1.1
      {
         FullUseageThreadsPerBlock = 256;
         MaxBlocksPerSM = 3;
      }   
   }   

Где Dummyимеет шаблонный тип, и я написал функции для возврата размеров общих шаблонных типов (например, int, float, double) в CUDA (например, в C и int обычно 16 бит, в CUDA его 32-битный).

Скопируйте массивы для умножения в глобальную память на устройстве.

Затем возьмите квадратный корень из вашей переменной NumberOfSharedValues и напишите ядро, которое объявляет три массива в разделяемой памяти этой длины (два"чанки" и продукт).

Пусть ядро ​​скопирует чанки для умножения в общую память, выполните умножение, а затем запишите полученный чанк "product" обратно в глобальную память.

Наконец, прочитайте глобальный массив продуктов обратно на хост.

Вуаля, я думаю, это должно помочь вам.

Помните, что вы будете назначать общий ресурс.Описанная выше память - это объем, доступный одному мультипроцессору (а поскольку каждый из ваших блоков выделяет столько памяти, то блоки = # мультипроцессоры), поэтому ваш общий размер элементов, которые вы можете обрабатывать одновременно, будет зависеть от того, сколькоу вас есть мультипроцессоры, опять же запрашиваемое количество.

Также помните, что вы можете использовать cuMemGetInfo, чтобы получить объем свободной памяти, чтобы убедиться, что вы можете поместить свой массив who в глобальную память.

Повторное чтение руководства и просмотр @ примеров, опубликованных NVIDIA, имеют решающее значение для понимания иерархии памяти на устройстве и написания хороших ядер.

Надеюсь, это поможет !!

...