Можно ли предварительно выделить массив с помощью gpuArray и получить разрешение на запись в него в настройках mexcuda?
Я написал фрагмент кода в MatLab (2018a), который представляет собой гибрид между стандартным кодом Matlab и кодом CUDA, который я связал, используя компиляцию с mexcuda. Основной цикл в моем коде содержит интерполяцию матрицы, скажем, от размера [n x m] до [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?
РЕДАКТИРОВАТЬ:
Приведенный ниже код показывает два примера кода на 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 ответ
Для вашего мекс-кода используйте 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, поскольку разделяете распараллеливаемые элементы.