我有这个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_k
和T_k-overhead
的值,我启动了两次内核,一个是仅占用矩阵的一行,其中T_k1
时间单位,另一个是方二矩阵的行,T_k2
时间单位。取差异就是内核每行矩阵的完成时间;从而,
T_k = T_k2 - T_k1
和T_k-overhead = 2*T_k1 - T_k2
。我认为在给定这些参数的情况下求解n
的上述等式会给出n
大于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利用率最高时,条件为真。
这实际上是你应该先做的事情:
为了最大限度地提高利用率,您需要增加n
,直到执行线性增加。您还需要通过改进内存模式来优化内核:
不是每个内核线程处理rowCount
并且在内存访问方面有一个步幅,你应该为每个线程设置一个矩阵元素,每个warp连续存储器访问。
这也会简化内核,这也经常会增加gpu的使用(例如每个warp使用更少的寄存器)
对于重叠执行和传输,您已经知道如何使用异步调用+流