2013-08-16 12 views
3

Данные в 3D-матрице генерировались слоями (сверху вниз), и я хочу умножить эти данные на двумерную матрицу B, но istead для взятия каждого слоя Мне нужно принять вектор из слоя 1, вектор из слоя 2 и т. д.Умножающая трехмерная матрица и 2D-матрица CUDA

В настоящее время, что я делаю это, чтобы скопировать эти векторы из 3D-матрицы на матрицу 2D TMPA затем умножить B (используя CUBLAS) и сохранение результатов в TMPB, наконец, скопировать обратно по строкам где это соответствует в 3D-матрице C.

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

Что было бы лучшим способом сделать это вычисление? Я думал о перестановке данных перед их умножением, чтобы избежать копий памяти.

3D-матрица и С и 2D матрицы В уже находятся в памяти графического процессора.

РЕДАКТИРОВАТЬ

Пусть М, Н, Р размеры матрицы 3D хранится в строке основного порядка в линейном массиве на память устройства. Мой код выглядит так:

cudaMalloc((void**)&d_tmpIn, sizeof(float)*M*P); 
cudaMalloc((void**)&d_tmpOut, sizeof(float)*M*P); 
cudaMalloc((void**)&d_C, sizeof(float)*M*N*P); 

for (int iN = 0; iN < N; iN++) 
{ 
    dst = d_tmpIn; 
    for (int iM = 0; iM < M; iM++) 
    { 
    cudaMemcpy(dst, &(d_A[iN*P+0+iM*N*P]), sizeof(float)*P, cudaMemcpyD2D); 
    dst += P; 
    } 

    cublasDgemm(cublasHandle, CUBLAS_OP_N, CUBLAS_OP_N, P, M, M, &alpha, d_tmpIn, P, d_B, M, &beta, d_tmpOut, P); 

    src = d_tmpOut; 
    for (int iM = 0; iM < M; iM++) 
    { 
    cudaMemcpy(&(d_C[iN*P+0+iM*N*P]), src, sizeof(float)*P, cudaMemcpyD2D); 
    src += P; 
    } 
} 

Надеюсь, что это поможет.

+0

Можете ли вы описать, как данные хранятся в памяти графического процессора и какие вызовы CUBLAS вы используете для этого расчета? Не совсем ясно, что вы на самом деле пытаетесь сделать из текста (подсказка: уравнения и короткие фрагменты кода стоят тысячи слов) – talonmies

+0

Обычно cudaMemcpyD2D должен быть довольно быстрым. Вы профилировали приложение, чтобы определить, где тратится время? –

+0

@RobertCrovella действительно они быстры, но мне было интересно, есть ли лучший способ сделать это, чтобы избежать этих копий памяти. Я посмотрю на предоставленный ответ и посмотрю, поможет ли это. – BRabbit27

ответ

4

Вам не нужно делать копии памяти! API BLAS и LAPACK были созданы таким образом, что вы можете указать начальную точку, длину шага, длину ведущих измерений и т. Д.

Таким образом, вы можете использовать 3D-массивы A и C как есть, но вызовите cublasDgemm, используя правильные параметры.

В вашем случае (если я правильно понимаю код), похоже, что каждая матрица должна быть P X M, и у вас есть N из них. Но похоже, что 3D-массив устроен как PxNxM. Поэтому, не выделяя память для d_tmpIn и d_tmpOut, вы можете сделать что-то вроде этого: Число строк A: P. число столбцов: M. Однако ведущий размер (lda) следует упомянуть как N * P. То же самое касается C.

int lda = N * P; 
int ldc = N * P; 
for (int iN = 0; iN < N; iN++) 
{ 
    double *d_tmpIn = d_A + iN * P; 
    double *d_tmpOut = d_C + iN * P; 
    cublasSetStream(streams[iN]); // Optional 
    cublasDgemm(cublasHandle, CUBLAS_OP_N, CUBLAS_OP_N, 
       P, M, M, &alpha, d_tmpIn, lda, d_B, M, &beta, d_tmpOut, ldc); 

} 

Вы также можете создавать потоки iN и запускать каждый прогон cublas в отдельном потоке. Обратите внимание, что это только будет полезно, если M и P достаточно малы (то есть ГПУ еще не насыщен вычислительно)

РЕДАКТИРОВАНИЕ Если вы планируете идти вперед с потоками, попробуйте создать их один раз на начале программы и повторного использования. Не создавайте и не уничтожайте потоки в том же цикле, что и Dgemm. Это увеличивает накладные расходы.

+0

Матрица должна быть 'MXP' и иметь N из них (я использую основной порядок строк и просто следую [этому руководству] (http://peterwittek.com/2013/06/cublas-matrix-c-style/) до избегайте перехода к основному столбцу), а 3D-массив расположен MxNxP. – BRabbit27

+0

Я думаю, что была ошибка с моей стороны, вместо умножения ** AB ** это должно быть ** BA ** (в противном случае размеры не согласуются). Путаница возникла из-за того, что с помощью кубласа мне пришлось инвертировать матрицы, чтобы избежать перехода от основной строки к основному порядку столбца (как описано в руководстве выше). В любом случае, я получил вашу мысль. Я попробую сразу же и вернусь с результатами/сомнениями. Благодаря ! – BRabbit27

+0

Я думал о том, как использовать потоки, но я не знаю, что вы подразумеваете под «достаточно маленьким». Обычно M составляет от 3 до 10, P составляет около 2e6 - 4e6, N может быть от 30 до 1800. Я профилировал приложение и, по-видимому, я не насыщал GPU, не могли бы вы дать больше советов по использованию потоков. Благодарю. – BRabbit27

Смежные вопросы