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

Начало работы со сравнительным тестированием графического процессора

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

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

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

Улучшайте производительность Используя вычисления одинарной точности

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

Типичные примеры вычислений, подходящих для вычисления с одинарной точностью на графическом процессоре, включают обработку изображений и машинное обучение, видят, например, 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. Можно вычислить фактор повышения производительности между одинарной точностью и двойной точностью можно следующим образом:

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

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

  • Разделите установленное значение гигафлопс одинарной точности на значение гигафлопс двойной точности.

Примечание

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

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

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

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

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

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

Усовершенствованные инструменты для того, чтобы улучшать производительность

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

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

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

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

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

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

Лучшые практики для того, чтобы улучшать производительность

Аппаратная конфигурация

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

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

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

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

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

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

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

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

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

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

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

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

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

Можно также измерить уровень с tic и toc. Однако, чтобы получить точную синхронизацию на графическом процессоре, необходимо ожидать операций, чтобы завершиться прежде, чем вызвать toc. Существует два способа сделать это. Можно вызвать gather на итоговом GPU вывод прежде, чем вызвать 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

Выполните эту функцию в центральном процессоре на данных конкретного размера и измерьте время выполнения с помощью функции timeit MATLAB. Функция 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 - цикл выполняет БПФ, умножение и обратные операции FFT на отдельных столбцах длины 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

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

Поиск и устранение проблем графические процессоры

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

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

Смотрите также

Похожие темы