在CUDA中重叠内核执行和数据传输的最佳数据大小

时间:2013-08-24 20:52:45

标签: cuda parallel-processing

我有这个cuda内核,它对方形矩阵的元素进行了平方,它非常有趣。我想使用3个cuda流并将输入矩阵分成多个块,以便我以循环方式使用流在给定块上执行H2D MemcpyAsync,内核启动和D2H MemcpyAsync。这是完整的源代码。

#include<iostream>
#include<vector>
#include<cuda.h>
#include<sys/time.h>
using namespace std;
__global__ void MatrixSquareKernel(int *inMatrix, int *outMatrix, size_t width, size_t     rowCount) {
    int myId = blockIdx.x * blockDim.x + threadIdx.x;
    size_t crntRow = 0;
    if(myId < width) {
            size_t mId;
            while(crntRow < rowCount) {
                    mId = myId * width + crntRow;enter code here
                    outMatrix[mId] = inMatrix[mId] * inMatrix[mId];
                    crntRow++;
            }
    }
 }
 int main() {

    size_t count = width * width;
    size_t size = count * sizeof(int);

    vector<cudaStream_t> streams(strCount);

    for(int i = 0; i < strCount; i++)
            cudaStreamCreate(&streams[i]);

    int *h_inMatrix, *h_outMatrix;
    int *d_inMatrix, *d_outMatrix;

    cudaHostAlloc((void **)&h_inMatrix, size, cudaHostAllocDefault);
    cudaHostAlloc((void **)&h_outMatrix, size, cudaHostAllocDefault);
    cudaMalloc((void **)&d_inMatrix, size);
    cudaMalloc((void **)&d_outMatrix, size);

    for(int i = 0; i = count; i++)
            h_inMatrix[i] = i;

    size_t optimalRows = 16;
    size_t iter = width/optimalRows + ((width % optimalRows == 0)? 0: 1);

    size_t chnkOffset, chnkSize, strId, sentRows;

    struct timeval start, stop;
    gettimeofday(&start, NULL);
    for(int i = 0; i < iter; i++){
            sentRows = i * optimalRows;
            chnkOffset = width * sentRows;
            chnkSize = width * optimalRows * sizeof(int);
            if(sentRows > width){
                    optimalRows -= sentRows - width; //Cutoff the extra rows in this chunk if it's larger than the remaining unsent rows
                    chnkSize = width * optimalRows * sizeof(int);
            }
            strId = i % strCount;
            cudaMemcpyAsync(d_inMatrix + chnkOffset, h_inMatrix + chnkOffset, chnkSize, cudaMemcpyHostToDevice, streams.at(strId));
            MatrixSquareKernel<<<1, width, 0, streams.at(strId)>>>(d_inMatrix + chnkOffset, d_outMatrix + chnkOffset, width, optimalRows);
            cudaMemcpyAsync(h_outMatrix + chnkOffset, d_outMatrix + chnkOffset, chnkSize, cudaMemcpyDeviceToHost, streams.at(strId));
    }
    cudaThreadSynchronize();
    gettimeofday(&stop, NULL);

    double elapsedTime = (stop.tv_sec - start.tv_sec) + (start.tv_usec - stop.tv_usec)/1e6;

    cout<<"Elapsed Time: "<<elapsedTime<<endl;


    for(int i = 0; i < strCount; i++)
            cudaStreamDestroy(streams[i]);

    cudaFreeHost(h_inMatrix);
    cudaFreeHost(h_outMatrix);
    cudaFree(d_inMatrix);
    cudaFree(d_outMatrix);

    return 0;
}

每个块包含一定数量的行,因此变量optimalRows。现在,我正在为它分配一个静态值。但我的目标是使用内核在矩阵的一行上的完成时间和矩阵行的传输时间来计算其值。我们假设这个值是n。为了计算它,我正在解决T_tr(n * width * sizeof(int)) = n * T_k + T_k-overhead的等式n,其中T_tr(M)M字节数据的传输时间,我可以通过考虑带宽来计算对于PCI / e总线,T_k是对矩阵的单行进行平方的完成时间,T_k-overhead是内核启动的成本。为了测量T_kT_k-overhead的值,我启动了两次内核,一个是仅占用矩阵的一行,其中T_k1时间单位,另一个是方二矩阵的行,T_k2时间单位。取差异就是内核每行矩阵的完成时间;从而, T_k = T_k2 - T_k1T_k-overhead = 2*T_k1 - T_k2。我认为在给定这些参数的情况下求解n的上述等式会给出n大于1的值,但它会给我一个小于1的值。

我错过了什么?我非常感谢你的想法。 感谢

1 个答案:

答案 0 :(得分:1)

  

我想在给定这些参数的情况下求解n的上述等式   会给我一个大于1的值,但它给了我   小于1的值。

您没有最小化T_tr,您只是在寻找满足n条件的T_tr

小于1的值会产生感觉。零值是一个明显的解决方案,并为您提供

T_tr(0) = T_k-overhead // always true

if {

} T_k-overhead = 2*T_k1 - T_k2也是正确的

N * size(T_k) == N * T_tr(T_k) // considering the problem perfectly linear

由于您的问题是线性的,因此当您的GPU利用率最高时,条件为真。

这实际上是你应该先做的事情:

  1. 最大化GPU利用率
  2. 重叠转移和执行
  3. 为了最大限度地提高利用率,您需要增加n,直到执行线性增加。您还需要通过改进内存模式来优化内核:

    不是每个内核线程处理rowCount并且在内存访问方面有一个步幅,你应该为每个线程设置一个矩阵元素,每个warp连续存储器访问。 这也会简化内核,这也经常会增加gpu的使用(例如每个warp使用更少的寄存器)

    对于重叠执行和传输,您已经知道如何使用异步调用+流