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