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

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

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

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

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

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

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

Для примера - функция foo содержит разделы кода, которые обрабатывают данные последовательно на центральном процессоре и параллельно на графическом процессоре.

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 Coder анализирует все использования и определения заданной переменной и использует флаги состояния для выполнения минимизации. Пример оригинального кода и того, как выглядит сгенерированный код, показан в этой таблице.

Оригинальный кодОптимизированный сгенерированный код
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;

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