exponenta event banner

Выделение и минимизация памяти графического процессора

Дискретные и управляемые режимы

GPU Coder™ предоставляет доступ к двум различным выделениям памяти (malloc) режимы, доступные в модели программирования CUDA ® ,cudaMalloc и cudaMallocManaged. cudaMalloc API применим к традиционно отдельным процессорам и глобальным запоминающим устройствам GPU. cudaMallocManaged применима к унифицированной памяти.

С точки зрения программиста, традиционная компьютерная архитектура требует, чтобы данные распределялись и совместно использовались между пространствами памяти CPU и GPU. Потребность в приложениях для управления передачей данных между этими двумя пространствами памяти увеличивает сложность. Унифицированная память создает пул управляемой памяти, совместно используемый ЦП и графическим процессором. Управляемая память доступна как для ЦП, так и для графического процессора через один указатель. Унифицированная память пытается оптимизировать производительность памяти путем переноса данных на нужное устройство, одновременно скрывая сведения о миграции из программы. Хотя унифицированная память упрощает модель программирования, она требует вызовов синхронизации устройств, когда данные, записанные на GPU, доступны на CPU. Кодер графического процессора вставляет эти вызовы синхронизации. Согласно NVIDIA ®, унифицированная память может обеспечить значительные преимущества в производительности при использовании CUDA 8.0 или при нацеливании на встроенное оборудование, такое как NVIDIA Tegra ®.

Чтобы изменить режим выделения памяти в приложении Кодер графического процессора, используйте Malloc Mode раскрывающийся список в разделе Дополнительные параметры - > Кодер графического процессора. При использовании интерфейса командной строки используйте MallocMode построить свойство конфигурации и установить для него значение 'discrete' или 'unified'.

Минимизация памяти

Кодер GPU анализирует зависимость данных между разделами CPU и GPU и выполняет оптимизацию для минимизации количества cudaMemcpy вызов функции в сгенерированном коде. Анализ также определяет минимальный набор местоположений для копирования данных между CPU и GPU с помощью cudaMemcpy.

Например, функция foo имеет разделы кода, которые обрабатывают данные последовательно на CPU и параллельно на GPU.

function [out] = foo(input1,input2)
	   …
     % CPU work
			input1 = …
			input2 = …
			tmp1 = …
			tmp2 = …
   	…
     % GPU work
			kernel1(gpuInput1, gpuTmp1);
       kernel2(gpuInput2, gpuTmp1, gpuTmp2);
       kernel3(gpuTmp1, gpuTmp2, gpuOut);

   	…
     % CPU work
       … = out

end

Неоптимизированная реализация CUDA потенциально может иметь несколько cudaMemcpy вызовы функций для передачи всех входов gpuInput1,gpuInput2и временные результаты gpuTmp1,gpuTmp2 между вызовами ядра. Потому что промежуточные результаты gpuTmp1,gpuTmp2 не используются вне графического процессора, они могут храниться в памяти графического процессора, что приводит к меньшему cudaMemcpy вызовы функций. Эти оптимизации повышают общую производительность созданного кода. Оптимизированная реализация:

gpuInput1 = input1;
gpuInput2 = input2;

kernel1<<< >>>(gpuInput1, gpuTmp1);
kernel2<<< >>>(gpuInput2, gpuTmp1, gpuTmp2);
kernel3<<< >>>(gpuTmp1, gpuTmp2, gpuOut);

out = gpuOut;

Устранение избыточности cudaMemcpy вызовы, кодер GPU анализирует все виды использования и определения данной переменной и использует флаги состояния для минимизации. В этой таблице приведен пример исходного кода и его внешнего вида.

Исходный кодОптимизированный сгенерированный код
A(:) = …
…
for i = 1:N
   gB = kernel1(gA);
   gA = kernel2(gB);

   if (somecondition)
      gC = kernel3(gA, gB);
   end
   …
end
…
… = C;
A(:) = …
A_isDirtyOnCpu = true;
…
for i = 1:N
   if (A_isDirtyOnCpu)
      gA = A;
      A_isDirtyOnCpu = false;
   end
   gB = kernel1(gA);
   gA = kernel2(gB);
   if (somecondition)
      gC = kernel3(gA, gB);
      C_isDirtyOnGpu = true;
   end
   …
end
…
if (C_isDirtyOnGpu)
   C = gC;
   C_isDirtyOnGpu = false;
end
… = C;

_isDirtyOnCpu флаг сообщает оптимизации памяти кодера GPU о подпрограммах, в которых данная переменная объявляется и используется либо на CPU, либо на затем GPU.