exponenta event banner

Измерение и повышение производительности графического процессора

Приступая к тестированию GPU

Для измерения производительности графического процессора в MATLAB ® можно использовать различные тесты:

  • Использовать gpuBench в MATLAB Central File Exchange для выполнения различных тестов, в том числе для выполнения ресурсоемких задач, как с одинарной, так и с двойной точностью. Сравните производительность видеокарты с вычислительной карточкой. Дополнительные сведения см. в разделе https://www.mathworks.com/matlabcentral/fileexchange/34080-gpubench.

  • Используйте paralleldemo_gpu_bench скрипт в разделе Измерение производительности графического процессора для получения информации о скорости шины PCI, производительности чтения/записи памяти графического процессора и пиковых расчетах для матричных вычислений с двойной точностью.

Повышение производительности с помощью вычислений с единой точностью

Производительность графического процессора можно повысить, выполнив вычисления с одной точностью, а не с двойной точностью. С другой стороны, в вычислениях CPU это улучшение не достигается при переключении с двойной на одинарную точность. Причина в том, что большинство GPU-карт разработаны для графического дисплея, требующего высокой одинарной точности производительности.

Типичные примеры вычислений, подходящих для вычисления с одной точностью на ГП, включают в себя обработку изображений и машинное обучение, см., например, https://www.mathworks.com/content/dam/mathworks/tag-team/Objects/d/Deep_Learning_in_Cloud_Whitepaper.pdf. Однако другие типы вычислений, такие как задачи линейной алгебры, обычно требуют обработки с двойной точностью.

В зависимости от карты графического процессора и общего количества ядер можно добиться повышения производительности в 50 раз по сравнению с вычислениями с двойной точностью. Вычислительные карты высшего класса обычно демонстрируют меньшее улучшение. Вы можете определить повышение производительности конкретного графического процессора с помощью gpuBench, см. https://www.mathworks.com/matlabcentral/fileexchange/34080-gpubench.

Полный обзор производительности плат графического процессора NVIDIA ® см. в разделе https://en.wikipedia.org/wiki/List_of_Nvidia_graphics_processing_units. Коэффициент повышения производительности между одной точностью и двойной точностью можно рассчитать следующим образом:

  • Найдите графический процессор на вики-странице выше.

  • Получите из таблицы указанные значения производительности с одинарной и двойной точностью. Если значение GFLOPS двойной точности отсутствует, предположим, что отношение 24‐32x медленнее для двойной точности.

  • Разделите заданное значение GFLOPS одиночной точности на значение GFLOPS двойной точности.

Примечание

Если в ноутбуке установлена мобильная графическая карта, ее можно использовать для вычислений с графическим процессором. Однако графический процессор ноутбука, скорее всего, будет гораздо менее мощным, чем эквивалентный настольный компьютер, и поэтому производительность снижается.

Базовый рабочий процесс для повышения производительности

Целью вычислений графического процессора в MATLAB является ускорение приложений. В этом разделе рассматриваются фундаментальные концепции и практики, которые могут помочь достичь более высокой производительности графического процессора, такие как конфигурация оборудования графического процессора и передовые практики в коде. В нем рассматривается компромисс между трудностью реализации и производительностью и описываются критерии, которые можно использовать для выбора между использованием функций gpuArray. arrayfun, MEX-файлы или ядра CUDA. Наконец, описывается, как точно измерить производительность графического процессора.

При преобразовании кода MATLAB в код, выполняемый на GPU, лучше всего начинать с кода MATLAB, который уже работает хорошо. Хотя GPU и CPU имеют разные характеристики производительности, общие рекомендации по написанию хорошего кода MATLAB также помогают написать хороший код MATLAB для графического процессора. Первым шагом почти всегда является профилирование кода ЦП. Строки кода, которые профилировщик показывает, занимая наибольшее время на ЦП, скорее всего, будут теми, на которых вы должны сосредоточиться при кодировании для графического процессора.

Проще всего начать преобразование кода с помощью встроенных функций MATLAB, поддерживающих данные gpuArray. Эти функции принимают входные данные gpuArray, выполняют вычисления на GPU и возвращают выходные данные gpuArray. Список функций MATLAB, поддерживающих данные gpuArray, содержится в разделе Выполнение функций MATLAB на графическом процессоре. Как правило, эти функции поддерживают те же аргументы и типы данных, что и стандартные функции MATLAB, которые вычисляются на CPU.

Если все функции, которые вы хотите использовать, поддерживаются на GPU, код выполнения на GPU может быть таким же простым, как и вызов gpuArray для передачи входных данных в графический процессор и вызова gather для извлечения выходных данных из графического процессора по завершении. Во многих случаях может потребоваться векторизация кода, замена закольцованных скалярных операций матрицей MATLAB и векторными операциями. Хотя векторизация обычно является хорошей практикой для ЦП, она обычно имеет решающее значение для достижения высокой производительности графического процессора. Дополнительные сведения см. в разделе Векторизация для повышения производительности графического процессора.

Расширенные инструменты для повышения производительности

Возможно, что даже после преобразования входных данных в gpuArrays и векторизации кода в алгоритме есть операции, которые либо не являются встроенными функциями, либо недостаточно быстры, чтобы соответствовать требованиям приложения. В таких ситуациях у вас есть три основных варианта: arrayfun для предварительной компиляции элементов приложения, использования функций библиотеки GPU или написания пользовательского ядра CUDA.

Если у вас есть чисто элементная функция, вы можете улучшить ее производительность, вызвав ее с помощью arrayfun. arrayfun функция на GPU превращает элементную функцию MATLAB в пользовательское ядро CUDA, тем самым уменьшая накладные расходы на выполнение операции. Часто существует подмножество вашего приложения, которое можно использовать с arrayfun даже если все приложение не может быть. В примере «Повышение производительности функций MATLAB ® по элементам на графическом процессоре с использованием ARRAYFUN» показаны основные концепции этого подхода; и пример Использование GPU ARRAYFUN для моделирования Монте-Карло показывает, как это может быть сделано в моделировании для финансового приложения.

MATLAB предоставляет обширную библиотеку функций с поддержкой графического процессора в Parallel Computing Toolbox™, Image Processing Toolbox™, Signal Processing Toolbox™ и других продуктах. Однако существует множество библиотек дополнительных функций, которые не имеют прямых встроенных аналогов в поддержке графического процессора MATLAB. Примеры включают библиотеку NVIDIA Performance Primitives и библиотеку CURAND, которые включены в набор инструментов CUDA, поставляемый с MATLAB. Если необходимо вызвать функцию в одной из этих библиотек, это можно сделать с помощью интерфейса MEX графического процессора. Этот интерфейс позволяет извлекать указатели на данные устройства из MATLAB gpuArrays, чтобы передать эти указатели функциям графического процессора. Возвращенные значения можно преобразовать в gpuArrays для возврата в MATLAB. Дополнительные сведения см. в разделе Запуск MEX-функций, содержащих код CUDA.

Наконец, можно записать пользовательское ядро CUDA для нужной операции. Такие ядра могут быть непосредственно интегрированы в MATLAB с помощью объекта CUDAKernel.

Пример иллюстрации трех подходов к вычислениям GPU: набор Мандельброта показывает, как реализовать простой расчет с использованием трех подходов, упомянутых в этом разделе. Этот пример начинается с кода MATLAB, который легко преобразуется для запуска на GPU, перезаписывает код для использования arrayfun для операций на основе элементов и, наконец, показывает, как интегрировать пользовательское ядро CUDA для той же операции.

Альтернативно, можно записать ядро CUDA как часть MEX-файла и вызвать его с помощью CUDA Runtime API внутри MEX-файла. Любой из этих подходов может позволить работать с низкоуровневыми функциями графического процессора, такими как общая память и текстурная память, которые недоступны непосредственно в коде MATLAB. Дополнительные сведения см. в примере Доступ к расширенным функциям CUDA с помощью MEX.

Рекомендации по повышению производительности

Конфигурация оборудования

Как правило, наилучшую производительность можно достичь, если графический процессор посвящен вычислениям. Обычно нецелесообразно использовать одно и то же устройство GPU как для вычислений, так и для графики, из-за объема памяти, занимаемой для проблем разумного размера и постоянного использования устройства системой для графики. По возможности получите отдельное устройство для графики. Сведения о настройке устройства для вычислений или графики зависят от версии операционной системы и драйвера.

В системах Windows ® GPU-устройство может находиться в одном из двух режимов: Windows Display Driver Model (WDDM) или Tesla Compute Cluster (TCC). Для лучшей производительности все устройства, используемые для вычислений, должны находиться в режиме TCC. Дополнительные сведения см. в документации NVIDIA.

Высокопроизводительные вычислительные устройства NVIDIA, линейка Tesla, поддерживают коды коррекции ошибок (ECC) при чтении и записи GPU-памяти. Целью ECC является исправление случайных битовых ошибок, которые обычно возникают при чтении или записи динамической памяти. Одним из способов повышения производительности является отключение ECC для увеличения достижимой полосы пропускания памяти. Хотя оборудование можно настроить таким образом, MathWorks не рекомендует эту практику. Потенциальная потеря точности из-за ошибок молчания может быть более вредной, чем выгода от производительности.

Методы кодирования MATLAB

В этом разделе описываются общие методы, помогающие повысить производительность графического процессора. Некоторые из этих советов применимы и при написании кода MATLAB для ЦП.

Данные в массивах MATLAB хранятся в основном порядке столбцов. Поэтому полезно работать вдоль первого или столбцового размера массива. Если одно измерение данных значительно длиннее других, можно достичь лучшей производительности, если сделать первое измерение. Аналогично, если вы часто работаете с определенным измерением, его лучше всего использовать в качестве первого измерения. В некоторых случаях, если последовательные операции нацелены на различные измерения массива, может быть полезно транспонировать или переставлять массив между этими операциями.

Графические процессоры достигают высокой производительности, вычисляя множество результатов параллельно. Таким образом, матричные и высокомерные операции с массивом обычно выполняют намного лучше, чем операции с векторами или скалярами. Вы можете достичь лучшей производительности, переписав петли, чтобы использовать операции более высокой размерности. Процесс пересмотра основанного на цикле скалярно-ориентированного кода для использования матричных и векторных операций MATLAB называется векторизацией. Дополнительные сведения см. в разделе Использование векторизации.

По умолчанию все операции в MATLAB выполняются в арифметике с плавающей запятой двойной точности. Однако большинство операций поддерживают различные типы данных, включая целочисленные и одинарные операции с плавающей запятой. Современные графические процессоры и процессоры обычно имеют гораздо более высокую пропускную способность при выполнении операций с одной точностью, а данные с плавающей запятой с одной точностью занимают меньше памяти. Если требования к точности приложения позволяют использовать плавающую точку с одинарной точностью, это может значительно повысить производительность кода MATLAB.

Графический процессор находится в конце механизма передачи данных, известного как шина PCI. Хотя эта шина является эффективной, высокоскоростной способ передачи данных из памяти хоста ПК на различные платы расширения, он все еще значительно медленнее, чем общая пропускная способность глобальной памяти устройства GPU или CPU (подробнее см. в примере Измерение производительности GPU). Кроме того, передача данных из устройства графического процессора в память хоста MATLAB приводит к тому, что MATLAB ожидает завершения всех ожидающих операций на устройстве перед выполнением любых других инструкций. Это может значительно снизить производительность приложения. Как правило, необходимо ограничить количество переносов данных между рабочим пространством MATLAB и графическим процессором. Если можно перенести данные в графический процессор один раз в начале приложения, выполните все расчеты, которые можно выполнить на графическом процессоре, а затем перенесите результаты обратно в MATLAB в конце, что обычно приводит к лучшей производительности. Аналогично, когда это возможно, это помогает создавать массивы непосредственно на GPU, используя либо 'gpuArray' или 'like' опция для таких функций, как zeros (например, Z = zeros(___,'gpuArray') или Z = zeros(N,'like',g) для существующего gpuArray g).

Измерение производительности графического процессора

Лучший способ измерения производительности графического процессора - это использование gputimeit. Эта функция принимает за вход дескриптор функции без входных аргументов и возвращает измеренное время выполнения этой функции. Он учитывает такие аспекты сравнительного анализа, как повторение временной операции для получения лучшего разрешения, выполнение функции перед измерением, чтобы избежать служебных данных инициализации, и вычитание служебных данных функции синхронизации. Также, gputimeit гарантирует, что все операции на GPU завершены до окончательного времени.

Например, рассмотрим измерение времени, затраченного на вычисление lu факторизация случайной матрицы A размера Nоколо-N. Это можно сделать, определив функцию, которая выполняет lu факторизация и передача дескриптора функции gputimeit:

A = rand(N,'gpuArray');
fh = @() lu(A);
gputimeit(fh,2); % 2nd arg indicates number of outputs

Вы также можете измерить производительность с помощью tic и toc. Однако для получения точной синхронизации на GPU перед вызовом необходимо дождаться завершения операций. toc. Есть два способа сделать это. Вы можете позвонить gather на конечном выходе графического процессора перед вызовом tocЭто вынуждает все вычисления завершаться до измерения времени. В качестве альтернативы можно использовать wait функция с gpuDevice объект в качестве его входных данных. Например, если вы хотите измерить время, затраченное на вычисление lu факторизация матрицы A использование tic, toc, и wait, вы можете сделать это следующим образом:

gd = gpuDevice();
tic();
[l,u] = lu(A);
wait(gd);
tLU = toc();

Можно также использовать профилировщик MATLAB для демонстрации распределения времени вычислений в коде графического процессора. Следует отметить, что для выполнения измерений синхронизации профилировщик выполняет каждую строку кода независимо, поэтому он не может учитывать перекрывающееся (асинхронное) выполнение, которое может произойти во время нормальной работы. Для синхронизации целых алгоритмов следует использовать tic и toc, или gputimeit, как описано выше. Кроме того, профиль может не дать правильных результатов для пользовательских функций MEX, если они выполняются асинхронно.

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

В этом примере показано, как повысить производительность за счет выполнения функции на графическом процессоре вместо ЦП и векторизации вычислений.

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

function y = fastConvolution(data,filter)
[m,n] = size(data);
% Zero-pad filter to the column length of data, and transform
filter_f = fft(filter,m);

% Create an array of zeros of the same size and class as data
y = zeros(m,n,'like',data);

% Transform each column of data
for ix = 1:n
    af = fft(data(:,ix));
    y(:,ix) = ifft(af .* filter_f);
end
end

Выполните эту функцию в CPU для данных определенного размера и измерьте время выполнения с помощью MATLAB. timeit функция. timeit функция учитывает общие соображения бенчмаркинга, такие как учет запуска и накладных расходов.

a = complex(randn(4096,100),randn(4096,100));  % Data input
b = randn(16,1);                               % Filter input
c = fastConvolution(a,b);                      % Calculate output
ctime = timeit(@()fastConvolution(a,b));       % Measure CPU time
disp(['Execution time on CPU = ',num2str(ctime)]);

На примере машины этот код отображает выходные данные:

Execution time on CPU = 0.019335

Теперь выполните эту функцию на графическом процессоре. Это можно легко сделать, изменив входные данные на gpuArrays, а не обычные массивы MATLAB. 'like' синтаксис, используемый при создании вывода внутри функции, гарантирует, что y будет gpuArray, если data является gpuArray.

ga = gpuArray(a);                              % Move array to GPU
gb = gpuArray(b);                              % Move filter to GPU
gc = fastConvolution(ga,gb);                   % Calculate on GPU
gtime = gputimeit(@()fastConvolution(ga,gb));  % Measure GPU time
gerr = max(max(abs(gather(gc)-c)));            % Calculate error
disp(['Execution time on GPU = ',num2str(gtime)]);
disp(['Maximum absolute error = ',num2str(gerr)]);

На том же компьютере этот код отображает выходные данные:

Execution time on CPU = 0.019335
Execution time on GPU = 0.027235
Maximum absolute error = 1.1374e-14

К сожалению, для этой проблемы графический процессор работает медленнее, чем ЦП. Причина в том, что for-loop выполняет операции БПФ, умножения и обратного БПФ для отдельных столбцов длиной 4096. Лучшим способом повышения производительности является векторизация кода таким образом, чтобы один вызов функции MATLAB выполнял больше вычислений. Операции FFT и IFFT просты в векторизации: fft(A) вычисляет БПФ каждого столбца матрицы A. С помощью функции двоичного скалярного расширения MATLAB можно одновременно выполнить умножение фильтра на каждый столбец матрицы. bsxfun. Векторизованная функция выглядит следующим образом:

function y = fastConvolution_v2(data,filter)
m = size(data,1);
% Zero-pad filter to the length of data, and transform
filter_f = fft(filter,m);

% Transform each column of the input
af = fft(data);

% Multiply each column by filter and compute inverse transform
y = ifft(bsxfun(@times,af,filter_f));
end

Выполните тот же эксперимент, используя векторизованную функцию:

a = complex(randn(4096,100),randn(4096,100));   % Data input
b = randn(16,1);                                % Filter input
c = fastConvolution_v2(a,b);                    % Calculate output
ctime = timeit(@()fastConvolution_v2(a,b));     % Measure CPU time
disp(['Execution time on CPU = ',num2str(ctime)]);

ga = gpuArray(a);                               % Move data to GPU
gb = gpuArray(b);                               % Move filter to GPU
gc = fastConvolution_v2(ga, gb);                % Calculate on GPU
gtime = gputimeit(@()fastConvolution_v2(ga,gb));% Measure GPU time
gerr = max(max(abs(gather(gc)-c)));             % Calculate error
disp(['Execution time on GPU = ',num2str(gtime)]);
disp(['Maximum absolute error = ',num2str(gerr)]);
Execution time on CPU = 0.010393
Execution time on GPU = 0.0020537
Maximum absolute error = 1.1374e-14

В заключение, векторизация кода помогает процессору и графическому процессору работать быстрее. Однако векторизация помогает версии GPU гораздо больше, чем CPU. Улучшенная версия CPU почти вдвое быстрее оригинальной; улучшенная версия GPU в 13 раз быстрее оригинальной. Код GPU стал на 40% медленнее, чем CPU в первоначальной версии, и примерно в пять раз быстрее в пересмотренной версии.

Устранение неполадок графических процессоров

Если в вашем компьютере имеется только один графический процессор, то, вероятно, ваша графическая карта также действует как ваша видеокарта. В этом случае ваш графический процессор, вероятно, зависит от времени ожидания, установленного операционной системой (ОС). Вы можете проверить это для вашего графического процессора следующим образом:

gpuDevice
ans =
...
KernelExecutionTimeout: 1
Если KernelExecutionTimeout = 1, то ваш GPU подлежит тайм-ауту, установленному ОС, гарантируя, что ОС всегда сможет печатать обновления на экране. Если расчет графического процессора занимает слишком много времени, операция прекращается. В этом случае для успешного возобновления вычислений графического процессора необходимо перезапустить MATLAB.

См. также

Связанные темы