是否可以在设备功能中同时启动多个流?

时间:2016-09-08 09:44:20

标签: cuda

属性:Win 10,VS 2013,CUDA 7.5,GeForce 920M 两种情况都没有任何错误或警告。两种情况的输出均为SAME。唯一的区别是:

enter image description here

在第二种情况下,Stream2和Stream3不存在。

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "../../common/common.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>

__global__ void kernel1(char *value){
    for (int i = 0; i < 100; i++){
        printf("%s\n", value);
    }
}

__global__ void kernel2(){
    cudaStream_t s1, s2;
    cudaStreamCreateWithFlags(&s1, cudaStreamNonBlocking);
    cudaStreamCreateWithFlags(&s2, cudaStreamNonBlocking);

    kernel1 << < 1, 1, 0, s1 >> >("up stream");
    kernel1 << < 1, 1, 0, s2 >> >("bottom stream");
}

int main(int argc, char **argv){
    printf("%s Starting...\n", argv[0]);    // set up device
    int dev = 0;
    cudaDeviceProp deviceProp;
    CHECK(cudaGetDeviceProperties(&deviceProp, dev));
    printf("Using Device %d: %s\n", dev, deviceProp.name);
    CHECK(cudaSetDevice(dev));
    // FIRST CASE
    //cudaStream_t s1, s2;
    //cudaStreamCreateWithFlags(&s1, cudaStreamNonBlocking);
    //cudaStreamCreateWithFlags(&s2, cudaStreamNonBlocking);
    //kernel1 << <1, 1, 0, s1 >> >();
    //kernel1 << <1, 1, 0, s2 >> >();

    //SECOND CASE
    kernel2 << < 1, 1>> >();

    CHECK(cudaDeviceSynchronize());
    CHECK(cudaGetLastError());  // check kernel error
    CHECK(cudaDeviceReset());   // reset device

    printf("\nEnd\n");
    getchar();
    return (0);
}

我还添加了common.h

#include <time.h>
#include <stdio.h>

#ifndef _COMMON_H
#define _COMMON_H

#define CHECK(call)                                                            \
{                                                                              \
    const cudaError_t error = call;                                            \
    if (error != cudaSuccess)                                                  \
    {                                                                          \
        fprintf(stderr, "Error: %s:%d, ", __FILE__, __LINE__);                 \
        fprintf(stderr, "code: %d, reason: %s\n", error,                       \
                cudaGetErrorString(error));                                    \
        getchar();exit(1);                                                               \
    }                                                                          \
}

#define CHECK_CUBLAS(call)                                                     \
{                                                                              \
    cublasStatus_t err;                                                        \
    if ((err = (call)) != CUBLAS_STATUS_SUCCESS)                               \
    {                                                                          \
        fprintf(stderr, "Got CUBLAS error %d at %s:%d\n", err, __FILE__,       \
                __LINE__);                                                     \
        getchar();exit(1);                                                               \
    }                                                                          \
}

#define CHECK_CURAND(call)                                                     \
{                                                                              \
    curandStatus_t err;                                                        \
    if ((err = (call)) != CURAND_STATUS_SUCCESS)                               \
    {                                                                          \
        fprintf(stderr, "Got CURAND error %d at %s:%d\n", err, __FILE__,       \
                __LINE__);                                                     \
        getchar();exit(1);                                                               \
    }                                                                          \
}

#define CHECK_CUFFT(call)                                                      \
{                                                                              \
    cufftResult err;                                                           \
    if ( (err = (call)) != CUFFT_SUCCESS)                                      \
    {                                                                          \
        fprintf(stderr, "Got CUFFT error %d at %s:%d\n", err, __FILE__,        \
                __LINE__);                                                     \
        getchar();exit(1);                                                               \
    }                                                                          \
}

#define CHECK_CUSPARSE(call)                                                   \
{                                                                              \
    cusparseStatus_t err;                                                      \
    if ((err = (call)) != CUSPARSE_STATUS_SUCCESS)                             \
    {                                                                          \
        fprintf(stderr, "Got error %d at %s:%d\n", err, __FILE__, __LINE__);   \
        cudaError_t cuda_err = cudaGetLastError();                             \
        if (cuda_err != cudaSuccess)                                           \
        {                                                                      \
            fprintf(stderr, "  CUDA error \"%s\" also detected\n",             \
                    cudaGetErrorString(cuda_err));                             \
        }                                                                      \
        getchar();exit(1);                                                               \
    }                                                                          \
}
clock_t seconds()
{
    return clock();
}
#endif // _COMMON_H

1 个答案:

答案 0 :(得分:1)

两种可能性:

  1. 当您对各种并发感兴趣时,Windows WDDM模式不是GPU的最佳操作模式。这在许多其他地方都有涉及,例如this recent SO question

  2. 您正在使用的工具(nsight VSE)可能无法向您提供最佳信息,否则您可能会误解它(您还没有提供您所看到的截图。)< / p>

  3. 你应该注意的另一件事是你的GT 920M(GK208)是一个相当低端的GPU,它可能在内核并发方面受到限制,因为它只有2个SM,但我不认为这是这个具体案例的限制因素。

    我修改了你发布的代码以取消注释kernel2的启动(因为那是你要问的那个)并在CUDA 7.5,Fedora 20,GT 640(也使用GK208)上运行它。没有做任何其他更改,这是我在linux上的nvvp中看到的输出:

    我们看到的是:

    enter image description here

    1. kernel1首先显示,在两个不同的流中启动两次。这对应于你的主机端启动kernel1,我们在这里看到内核并发。

    2. 之后,kernel2从主机启动。它会根据您的代码启动到默认流中,并以nvvp的方式显示。此外,我们注意到nvvp 计算时间轴中的kernel2在其持续时间的第一部分显示为实心条,在其持续时间的最后部分显示为空心条。这表示kernel2的所有线程都已在实心栏的末尾完成,但由于implicit child kernel synchronization associated with CDP而导致kernel2完成。

    3. 在这个&#34;空心区域&#34;在时间轴上的kernel2,我们还看到了2个kernel1的新实例。这两个新实例相互重叠,表明正确的并发性,并且在这两个子内核完成时,父内核2启动也完成。

    4. 基本上,当我使用nvvp在linux上运行它时,一切看起来都是正确的。如果你想以无限的方式探索并发,我绝对推荐Windows TCC模式或Linux over Windows WDDM模式。但是,大多数GeForce GPU无法进入TCC模式。您可能会问为什么子内核启动时没有为它们确定单独的流时间轴。我不知道答案,但我认为这是某种工具限制。可能还有一个&#34;演示文稿&#34;问题:父内核属于默认流,因此它的子内核也会这样做(即使你必须为它们创建单独的流以实现并发目的)。如果这让您感到困扰,您可以考虑在developer.nvidia.com上提交增强请求(包含关键字RFE的错误)