Можно ли предварительно выделить массив с помощью gpuArray и получить разрешение на запись в него в настройках mexcuda? - PullRequest
0 голосов
/ 07 июня 2018

Я написал фрагмент кода в MatLab (2018a), который является гибридом между стандартным кодом Matlab и кодом CUDA, который я связал с помощью компиляции с mexcuda.Основной цикл в моем коде содержит интерполяцию матрицы, скажем, от размера [nxm] до [N x M].Я ускорил эту часть с помощью графического процессора.Поскольку эта интерполяция находится в цикле, и поскольку размеры матриц, которые я интерполирую (как до, так и после), одинаковы на каждой итерации цикла, я хочу ускорить приложение, предварительно выделив массив выходного размера наGPU.Поэтому я хочу сделать что-то вроде: zeros(N,M,'gpuArray') один раз в начале, предоставить его в качестве входных данных для функции mexFunction и записать интерполированную матрицу в этот массив.Это сэкономит немало времени на выделение ([N_iterations-1] * alloc_time, грубо говоря).

Моя проблема сейчас такова: я не могу понять, возможно ли это.Используя mexFunction () в качестве точки входа, единственный известный мне способ получения входных массивов - это использовать что-то вроде:

mxGPUArray const *in  = mxGPUCreateFromMxArray(prhs[0]);
float const *dev_in  = (float const *)(mxGPUGetDataReadOnly(in));

, но, как следует из названия, это дает разрешение только на чтение,Я не могу использовать mxGPUGetData(in), потому что mxGPUArray равен const, с ним нельзя инициализировать неконстантную сущность.Кто-нибудь знает, есть ли способ обойти эту проблему, которая не включает выделение массива внутри mexFunction?

EDIT:

Код ниже показывает два примера кода C, где первыйаналогия с моим текущим кодом, а вторая - это то, к чему я стремлюсь:

Текущий:

#include "stdio.h"
int main(const int argc, const char *argv[]) {
// Allocate input matrix and fill from input arguments
FILE *fPtr; fPtr = fopen(argv[1],"rb");
double *mat_in = malloc(n*m*sizeof(*mat_in));
mat_in = fread(mat_in, sizeof(*mat_in), n*m, fPtr);
fclose(fPtr);

double *mat_out;
for (int it = 0, it < 1000, it++) {
    // Allocate output array and fill it;
    mat_out = malloc(N*M*sizeof(*mat_out));
    interpolation_function(mat_in, mat_out);

    // Do stuff with mat_out
    free(mat_out);
}
// Free mat_in, do more stuff and/or exit program

Идея:

#include "stdio.h"
int main(const int argc, const char *argv[]) {
// Allocate input matrix and fill from input arguments
FILE *fPtr; fPtr = fopen(argv[1],"rb");
double *mat_in = malloc(n*m*sizeof(*mat_in));
mat_in = fread(mat_in, sizeof(*mat_in), n*m, fPtr);
fclose(fPtr);

// Allocate output array once at the start:
double *mat_out = malloc(N*M*sizeof(*mat_out));

for (int it = 0, it < 1000, it++) {
    interpolation_function(mat_in, mat_out); // Fills mat_out
    // Do stuff with mat_out here;
}
free(mat_out);
// Free mat_in, do more stuff and/or exit program

Выше приведены два ((по крайней мере, на мой взгляд) аналогия для следующего гибридного кода Matlab-Cuda:

Ток (Matlab);функция mexcuda должна выделить память для интерполяции ввода (:,:, indx)

accumresult = zeros(N,M);
input = randn(100,100,1000);
for indx = 1:1000
    input_slice = mexcuda_interpolation( input(:,:,indx) );
    accumresult = accumresult + foo( input_slice, other_parameters);
end

Идея: выделение памяти перемещено из функции mexcuda (и, следовательно, из цикла ядра)и функции mexcuda нужно только получить указатель на этот (доступный для записи) массив;

accumresult = zeros(N,M,'gpuArray');
placeholder = zeros(N,M,'gpuArray'); % Memory allocated on GPU once here
input = randn(100,100,1000);
for indx = 1:1000
    accumresult = accumresult + foo( mexcuda_interpolation(input(:,:,indx)), placeholder, other_parameters);
    %mexcuda_interpolation() somehow gets a pointer to the allocated memory which it can write to
end

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

1 Ответ

0 голосов
/ 07 июня 2018

Для вашего mex-кода используйте mxGPUCreateGPUArray вместо mxGPUCreateFromMxArray для выделения памяти без инициализации.


О вашем коде MATLAB: почему вы делаете предварительное распределение?Понять принципы того, что вы делаете, потому что вам это нужно для работы с графическими процессорами.

В MATLAB, если вы не выделяете заранее, каждый раз, когда вы добавляете новые данные, MATLAB делает это под капотом: создает новый массив с новым размером, копирует данные из меньшего старого массива в новый.Конечно, это обескураживает, поскольку вы все время делаете ненужные копии.

В CUDA это невозможно.Динамические массивы не существуют.Тем более, что все, что вы делаете, не происходит последовательно, в цикле for, это происходит «одновременно».Поэтому очень важно знать размер вывода при выполнении операции.

Поэтому, когда у вас есть массивы GPU A и B, и вы оперируете f() над ними, f необходимо знать размер вывода.Если вы делаете в MATLAB C=f(A,B), вам не нужно предварительно выделять C (на самом деле, с этим примером, как и вы, без вычислений на GPU).MATLAB будет достаточно умен, чтобы сделать это для вас.

Итак, либо вам нужно понять, почему в следующем коде предварительное выделение C является пустой тратой времени

A=rand(N,M); % or A=rand(N,M,'gpuArray');
B=rand(N,M); % or B=rand(N,M,'gpuArray');
C=A+B;

Или, альтернативно, у вас есть код, который выглядит следующим образом:

A=rand(N,M,'gpuArray');
B=rand(N,M,'gpuArray');
for ii=1:N
   for jj=1:M
       C(ii,jj)=A(ii,jj)+B(ii,jj);
   end
end

Что по сути означает, что вы не получаете никакой выгоды от вычислений на GPU, поскольку разделяете распараллеливаемые элементы.

...