簡體   English   中英

CUBLAS 矩陣乘法與行主數據無轉置

[英]CUBLAS matrix multiplication with row-major data without transpose

我目前正在嘗試在我的 GPU 上使用 CUBLAS 實現矩陣乘法。

它適用於方矩陣和特定大小的輸入,但對於其他輸入,最后一行不返回(並且包含 0,因為這是我實現它的方式)。

我認為這是cublasSgemm的分配或語法問題,但我找不到它在哪里。

注意:如果您不熟悉 CUBLAS:它是column-majored ,這就是為什么看起來操作是以另一種方式執行的。

任何幫助,將不勝感激。


代碼:

請注意, gpuErrchkcublasErrchk在這里當然是無關緊要的。

#include <cuda.h>
#include <cuda_runtime.h>
#include <cublas_v2.h>

#include <vector>

std::vector<float> CUDA_mult_MAT(const std::vector<float> &data_1 , const uint64_t data_1_rows, const uint64_t data_1_columns,
                                 const std::vector<float> &data_2 , const uint64_t data_2_rows, const uint64_t data_2_columns){

    cublasHandle_t handle;

    cublasErrchk(cublasCreate(&handle));

    std::vector<float> result(data_1_rows * data_2_columns); //Vector holding the result of the multiplication

    /*----------------------------------------------------------------------------------------------*/

    float* GPU_data_1 = NULL;
    gpuErrchk(cudaMalloc((void**)&GPU_data_1 , data_1.size()*sizeof(float))); //Allocate memory on the GPU
    gpuErrchk(cudaMemcpy(GPU_data_1, data_1.data(), data_1.size()*sizeof(float), cudaMemcpyHostToDevice)); //Copy data from data_1 to GPU_data_1

    float* GPU_data_2 = NULL;
    gpuErrchk(cudaMalloc((void**)&GPU_data_2 ,data_2.size()*sizeof(float))); //Allocate memory on the GPU
    gpuErrchk(cudaMemcpy(GPU_data_2, data_2.data(), data_2.size()*sizeof(float), cudaMemcpyHostToDevice));//Copy data from data_2 to GPU_data_2

    float* GPU_result = NULL;
    gpuErrchk(cudaMalloc((void**)&GPU_result , result.size()*sizeof(float))); //Allocate memory on the GPU

    /*----------------------------------------------------------------------------------------------*/


    const float alpha = 1.f; 
    const float beta = 0.f;

    cublasErrchk(
               cublasSgemm(handle , CUBLAS_OP_N , CUBLAS_OP_N,
                           data_2_columns , data_2_rows ,data_1_columns,
                           &alpha , GPU_data_2 , data_2_columns,
                           GPU_data_1 , data_1_columns,
                           &beta , GPU_result , data_1_rows)
             ); //Perform multiplication 



    gpuErrchk(cudaMemcpy(result.data() , GPU_result , result.size() * sizeof(float) , cudaMemcpyDeviceToHost)); //Copy back to the vector 'result'

    gpuErrchk(cudaFree(GPU_data_1)); //Free GPU memory
    gpuErrchk(cudaFree(GPU_data_2)); //Free GPU memory
    gpuErrchk(cudaFree(GPU_result)); //Free GPU memory

    cublasErrchk(cublasDestroy_v2(handle)); 


    return result;


}


輸入:


#include <iostream>

#include <vector>

int main(){

    const std::vector<float> r1 =  CUDA_mult_MAT({1 , 2 , 3 , 4 , 5 , 6} , 2 , 3 ,
                                           {7 , 8 , 9 , 10 , 11 , 12} , 3 , 2);
/*
Product :
         7  8
1 2 3    9  10
4 5 6    11 12

*/

    for(auto & value: r1){std::cout << value << " " ;}
    std::cout << std::endl;

    const std::vector<float> r2 =  CUDA_mult_MAT({7 , 8 , 9 , 10 , 11 , 12} , 3 , 2 ,
                                           {1 , 2 , 3 , 4 , 5 , 6} , 2 , 3);
/*
Product :
7  8   
9  10   1  2  3
11 12   4  5  6
*/


    for(auto & value: r2){std::cout << value << " " ;}
    std::cout << std::endl;

    return 0;
}

輸出:

程序打印:

58 64 139 154 
39 54 69 49 68 87 0 0 0
                  ^~~~~~~

預期的:

58 64 139 154 
39 54 69 49 68 87 59 82 105
                  ^~~~~~~

我們可以通過不同方式觀察您的 CUBLAS 使用問題。

首先,研究CUBLAS Sgemm 文檔,我們看到 3 個參數mnk出現緊接在轉置說明符之后:

cublasStatus_t cublasSgemm(cublasHandle_t handle,
                       cublasOperation_t transa, cublasOperation_t transb,
                       int m, int n, int k, 
                           ^      ^      ^

我們還觀察到矩陣維度由下式給出:

A、B 和 C 是以列優先格式存儲的矩陣,維度為 op ( A ) m × k、op ( B ) k × n 和 C m × n,

所以第一個輸入矩陣的維度為mxk第二個輸入矩陣的維度為kxn ,而 output 矩陣的維度為mxn

讓我們暫時關注 output 矩陣。 鑒於它的維度是使用mn參數指定的,它不可能是正確的(讓我們說在非正方形的情況下)為那些傳遞data_2維度:

           cublasSgemm(handle , CUBLAS_OP_N , CUBLAS_OP_N,
                       data_2_columns , data_2_rows ,data_1_columns,
                       ^^^^^^^^^^^^^^   ^^^^^^^^^^^

其次,從錯誤檢查的角度來看,您可以通過使用cuda-memcheck運行您的代碼來快速估計您的 CUBLAS 調用有問題。 報告的第一個錯誤如下:

$ cuda-memcheck ./t23
========= CUDA-MEMCHECK
========= Invalid __global__ read of size 4
=========     at 0x000006f0 in void gemmSN_NN_kernel<float, int=256, int=4, int=2, int=8, int=3, int=4, bool=0, cublasGemvTensorStridedBatched<float const >, cublasGemvTensorStridedBatched<float>>(cublasGemmSmallNParams<float const , cublasGemvTensorStridedBatched<float const >, float>)
=========     by thread (64,0,0) in block (0,0,0)
=========     Address 0x7f9c30a2061c is out of bounds
=========     Device Frame:void gemmSN_NN_kernel<float, int=256, int=4, int=2, int=8, int=3, int=4, bool=0, cublasGemvTensorStridedBatched<float const >, cublasGemvTensorStridedBatched<float>>(cublasGemmSmallNParams<float const , cublasGemvTensorStridedBatched<float const >, float>) (void gemmSN_NN_kernel<float, int=256, int=4, int=2, int=8, int=3, int=4, bool=0, cublasGemvTensorStridedBatched<float const >, cublasGemvTensorStridedBatched<float>>(cublasGemmSmallNParams<float const , cublasGemvTensorStridedBatched<float const >, float>) : 0x6f0)
=========     Saved host backtrace up to driver entry point at kernel launch time
=========     Host Frame:/usr/lib/x86_64-linux-gnu/libcuda.so.1 (cuLaunchKernel + 0x2b8) [0x1e5cc8]
=========     Host Frame:/usr/local/cuda/lib64/libcublasLt.so.11 [0x1063c8b]
=========     Host Frame:/usr/local/cuda/lib64/libcublasLt.so.11 [0x10a9965]
=========     Host Frame:/usr/local/cuda/lib64/libcublasLt.so.11 [0x6bfacc]
=========     Host Frame:/usr/local/cuda/lib64/libcublasLt.so.11 [0x5fc7af]
=========     Host Frame:/usr/local/cuda/lib64/libcublasLt.so.11 [0x436c35]
=========     Host Frame:/usr/local/cuda/lib64/libcublasLt.so.11 (cublasLtMatmul + 0x60f) [0x43484f]
=========     Host Frame:/usr/local/cuda/lib64/libcublas.so.11 [0x9ef6db]
=========     Host Frame:/usr/local/cuda/lib64/libcublas.so.11 [0x50e4f0]
=========     Host Frame:/usr/local/cuda/lib64/libcublas.so.11 (cublasSgemm_v2 + 0x1ee) [0x50f29e]
=========     Host Frame:./t23 [0x7986]
=========     Host Frame:./t23 [0x7b4c]
=========     Host Frame:/lib/x86_64-linux-gnu/libc.so.6 (__libc_start_main + 0xe7) [0x21b97]
=========     Host Frame:./t23 [0x744a]
=========

當然,一種可能的解決方案是轉置輸入矩陣,因此它們按列主要順序排列,CUBLAS 提供了Sgemm的選項來做到這一點(見上文)。 但是在我看來,您要做的是在不轉置輸入 arrays 的情況下進行 C 樣式的行主乘法。這里有一篇文章描述了如何做到這一點。

當我將該啟發式方法應用於您的cublasSgemm()調用時,我得到以下信息:

           cublasSgemm(handle , CUBLAS_OP_N , CUBLAS_OP_N,
                       data_2_columns , data_1_rows ,data_1_columns,
                       &alpha , GPU_data_2 , data_2_columns,
                       GPU_data_1 , data_1_columns,
                       &beta , GPU_result , data_2_columns)

當我使用這些更改編譯並運行您的代碼時,我得到以下信息:

$ cuda-memcheck ./t23
========= CUDA-MEMCHECK
58 64 139 154
39 54 69 49 68 87 59 82 105
========= ERROR SUMMARY: 0 errors

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM