CUDA内核不重叠

CUDA内核不重叠,cuda,parallel-processing,gpu,nvvp,Cuda,Parallel Processing,Gpu,Nvvp,我有一个简单的向量乘法内核,我对2个流执行它。但当我在NVVP中分析时,内核似乎并没有重叠。是因为每个内核执行都使用了100%的GPU吗?如果不是,原因是什么 源代码: #include "common.h" #include <cstdlib> #include <stdio.h> #include <math.h> #include "cuda_runtime.h" #include "device_launch_parameters.h" #inclu

我有一个简单的向量乘法内核,我对2个流执行它。但当我在NVVP中分析时,内核似乎并没有重叠。是因为每个内核执行都使用了100%的GPU吗?如果不是,原因是什么

源代码:

#include "common.h"
#include <cstdlib>
#include <stdio.h>
#include <math.h>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "cuda_profiler_api.h"
#include <string.h>

const int N = 1 << 20;

__global__ void kernel(int n, float *x, float *y)
{
    int i = blockIdx.x*blockDim.x + threadIdx.x;
    if (i < n) y[i] = x[i] * y[i];
}

int main()
{

    float *x, *y, *d_x, *d_y, *d_1, *d_2;
    x = (float*)malloc(N*sizeof(float));
    y = (float*)malloc(N*sizeof(float));

    cudaMalloc(&d_x, N*sizeof(float));
    cudaMalloc(&d_y, N*sizeof(float));
    cudaMalloc(&d_1, N*sizeof(float));
    cudaMalloc(&d_2, N*sizeof(float));

    for (int i = 0; i < N; i++) {
        x[i] = 1.0f;
        y[i] = 2.0f;
    }

    cudaMemcpy(d_x, x, N*sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_y, y, N*sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_1, x, N*sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_2, y, N*sizeof(float), cudaMemcpyHostToDevice);

    const int num_streams = 8;

    cudaStream_t stream1;
    cudaStream_t stream2;

    cudaStreamCreateWithFlags(&stream1, cudaStreamNonBlocking);
    cudaStreamCreateWithFlags(&stream2, cudaStreamNonBlocking);

    cudaEvent_t start, stop;
    float elapsedTime;

    cudaEventCreate(&start);
    cudaEventRecord(start, 0);

    for (int i = 0; i < 300; i++) {
        kernel << <512, 512, 0, stream1 >> >(N, d_x, d_y);
        kernel << <512, 512, 0, stream2 >> >(N, d_1, d_2);
    }

    cudaStreamSynchronize(stream1);
    cudaStreamSynchronize(stream2);
    // cudaDeviceSynchronize();

    cudaEventCreate(&stop);
    cudaEventRecord(stop, 0);
    cudaEventSynchronize(stop);
    cudaEventElapsedTime(&elapsedTime, start, stop);
    printf("Elapsed time : %f ms\n", elapsedTime);

    cudaDeviceReset();
    cudaProfilerStop();
    return 0;
}

内核不重叠的原因是,gpu中充满了@Robert Crovella提到的执行线程。查看中的“计算能力”一章,对于您的CC(5.0),每个SM的线程数限制为2048个。你有5个SM,这就足够了 设备上最多可同时运行10240个线程。您只需要一个内核调用就可以调用512x512=262144个线程,这几乎没有为其他内核调用留下任何空间

您需要启动足够小的内核,以便2可以在您的设备上同时运行

我不是流方面的专家,但据我所知,如果你想用流运行你的程序,你需要把它分成块,你必须计算一个适当的偏移机制,以便你的流能够访问它们适当的数据。在当前代码中,您正在启动的每个流对完全相同的数据执行完全相同的计算。您必须在流之间分割数据

除此之外,如果希望获得最大性能,则需要将内核执行与异步数据传输重叠。要做到这一点,最简单的方法是将如下所示的方案分配给您的每个流

for(int i=0;i
这个配置只是告诉每个流执行memcpy,然后对一些数据执行内核,然后将数据复制回来。异步调用之后,流将同时工作并完成其任务


PS:我还建议您修改内核。使用一个线程只计算一次乘法是一种过度使用。我会使用线程来处理更多的数据。

内核不重叠的原因是,gpu中充满了@Robert Crovella提到的执行线程。查看中的“计算能力”一章,对于您的CC(5.0),每个SM的线程数限制为2048个。你有5个SM,这就足够了 设备上最多可同时运行10240个线程。您只需要一个内核调用就可以调用512x512=262144个线程,这几乎没有为其他内核调用留下任何空间

您需要启动足够小的内核,以便2可以在您的设备上同时运行

我不是流方面的专家,但据我所知,如果你想用流运行你的程序,你需要把它分成块,你必须计算一个适当的偏移机制,以便你的流能够访问它们适当的数据。在当前代码中,您正在启动的每个流对完全相同的数据执行完全相同的计算。您必须在流之间分割数据

除此之外,如果希望获得最大性能,则需要将内核执行与异步数据传输重叠。要做到这一点,最简单的方法是将如下所示的方案分配给您的每个流

for(int i=0;i
这个配置只是告诉每个流执行memcpy,然后对一些数据执行内核,然后将数据复制回来。异步调用之后,流将同时工作并完成其任务


PS:我还建议您修改内核。使用一个线程只计算一次乘法是一种过度使用。我会使用线程来处理更多的数据。

这可能与每个内核的100%利用率有关。流用于与数据操作重叠的内核执行。如果每个内核调用都充分利用您的gpu,那么内核就不会重叠。您的内核将启动512个块,每个块包含512个线程。第一次内核启动“填充”机器,第二次等待第一次。为了见证两个内核实际并发执行,这意味着这些内核在使用的机器资源方面会有很大的限制。如果一个内核使用了所有的机器资源,那么第二个内核将等待。这在概念上类似于不是一个大型网格的所有块都将同时执行的想法。您只能在特定GPU上看到SMs支持的尽可能多的块。@GregK。如果你想提供答案,我会投赞成票。@GregK。非常感谢您的评论,您能为我编辑的问题推荐一种更好的方法吗?鉴于您当前的代码使GPU在100%的时间内保持忙碌,您在寻求什么样的改进?是否有证据表明您的绩效低于预期?在优化占用率方面:对于处理1D阵列,通常最好将线程块中的线程数保持在相当低的水平。在多个GPU架构中,每个块256个线程通常是一个很好的折衷方案。同时,您可能希望运行至少20个x#SM线程块。这可能与每个内核的100%利用率有关。流用于与数据操作重叠的内核执行。如果每个内核调用都充分利用您的gpu,那么内核就不会重叠。您的内核将启动512个块,每个块包含512个线程。第一次内核启动“填充”了
CUDA Device Query...
There are 1 CUDA devices.

CUDA Device #0
Major revision number:         5
Minor revision number:         0
Name:                          GeForce GTX 850M
Total global memory:           0
Total shared memory per block: 49152
Total registers per block:     65536
Warp size:                     32
Maximum memory pitch:          2147483647
Maximum threads per block:     1024
Maximum dimension 0 of block:  1024
Maximum dimension 1 of block:  1024
Maximum dimension 2 of block:  64
Maximum dimension 0 of grid:   2147483647
Maximum dimension 1 of grid:   65535
Maximum dimension 2 of grid:   65535
Clock rate:                    901500
Total constant memory:         65536
Texture alignment:             512
Concurrent copy and execution: Yes
Number of multiprocessors:     5
Kernel execution timeout:      Yes
for (int i = 0; i < nStreams; ++i) {
     int offset = i * streamSize;
     cudaMemcpyAsync(&d_a[offset], &a[offset], streamBytes,        cudaMemcpyHostToDevice, stream[i]);
     kernel<<<streamSize/blockSize, blockSize, 0, stream[i]>>>(d_a, offset);
     cudaMemcpyAsync(&a[offset], &d_a[offset], streamBytes, cudaMemcpyDeviceToHost, stream[i]);
}