Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/129.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何并行运行Cuda内核调用和CPU函数?_C++_Cuda_Openmp - Fatal编程技术网

C++ 如何并行运行Cuda内核调用和CPU函数?

C++ 如何并行运行Cuda内核调用和CPU函数?,c++,cuda,openmp,C++,Cuda,Openmp,我有一个程序在GPU上运行,使用CUDA和许多小内核,这意味着在我的CPU上的内核调用需要与在我的GPU上的内核执行大约相同的时间 我想在我的程序循环中添加一个CPU函数,它需要的时间大约与我所有内核的一次迭代相同。我知道,在内核启动后,CPU可以与GPU异步工作,但因为我上次启动内核的时间并不比GPU的时间提前多少,所以在这种情况下,这是没有选择的 所以,我的想法是使用多个线程: 一个线程启动我的GPU内核,另一个线程(或多个其他线程)执行CPU功能并并行运行这两个线程 我创建了一个小示例来测

我有一个程序在GPU上运行,使用CUDA和许多小内核,这意味着在我的CPU上的内核调用需要与在我的GPU上的内核执行大约相同的时间

我想在我的程序循环中添加一个CPU函数,它需要的时间大约与我所有内核的一次迭代相同。我知道,在内核启动后,CPU可以与GPU异步工作,但因为我上次启动内核的时间并不比GPU的时间提前多少,所以在这种情况下,这是没有选择的

所以,我的想法是使用多个线程: 一个线程启动我的GPU内核,另一个线程(或多个其他线程)执行CPU功能并并行运行这两个线程

我创建了一个小示例来测试这个想法:

#include <unistd.h>
#include <cuda_runtime.h>
#include <cuda_profiler_api.h>

#define THREADS_PER_BLOCK 64

__global__ void k_dummykernel1(const float* a, const float* b, float* c, const int N)
{
    const int id = blockIdx.x * blockDim.x + threadIdx.x;
    if(id < N)
    {
        float ai = a[id];
        float bi = b[id];

        c[id] = powf(expf(bi*sinf(ai)),1.0/bi);
    }
}

__global__ void k_dummykernel2(const float* a, const float* b, float* c, const int N)
{
    const int id = blockIdx.x * blockDim.x + threadIdx.x;
    if(id < N)
    {
        float bi = b[id];

        c[id] = powf(c[id],bi);
    }
}

__global__ void k_dummykernel3(const float* a, const float* b, float* c, const int N)
{
    const int id = blockIdx.x * blockDim.x + threadIdx.x;
    if(id < N)
    {
        float bi = b[id];

        c[id] = logf(c[id])/bi;
    }
}

__global__ void k_dummykernel4(const float* a, const float* b, float* c, const int N)
{
    const int id = blockIdx.x * blockDim.x + threadIdx.x;
    if(id < N)
    {

        c[id] = asinf(c[id]);
    }
}

int main()
{
    int N = 10000;
    int N2 = N/5;

    float *a = new float[N];
    float *b = new float[N];
    float *c = new float[N];

    float *d_a,*d_b,*d_c;

    for(int i = 0; i < N; i++)
    {
        a[i] = (10*(1+i))/(float)N;
        b[i] = (i+1)/50.0;
    }



    cudaMalloc((void**)&d_a,N*sizeof(float));
    cudaMalloc((void**)&d_b,N*sizeof(float));
    cudaMalloc((void**)&d_c,N*sizeof(float));

    cudaMemcpy(d_a, a ,N*sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_b, b ,N*sizeof(float), cudaMemcpyHostToDevice);


    cudaProfilerStart();


    for(int k = 0; k < 100; k++)
    {

        k_dummykernel1<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
        k_dummykernel2<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
        k_dummykernel3<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
        k_dummykernel4<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);

        k_dummykernel1<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
        k_dummykernel2<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
        k_dummykernel3<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
        k_dummykernel4<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);

        for(int i = 0; i < N2; i++)
        {
            c[i] = pow(a[i],b[i]);
        }

    }

    cudaDeviceSynchronize();
    usleep(40000);

    for(int k = 0; k <= 100; k++)
    {

#pragma omp parallel sections num_threads(2)
        {
#pragma omp section
            {
                k_dummykernel1<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
                k_dummykernel2<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
                k_dummykernel3<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
                k_dummykernel4<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);

                k_dummykernel1<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
                k_dummykernel2<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
                k_dummykernel3<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
                k_dummykernel4<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
            }

#pragma omp section
            {
                for(int i = 0; i < N2; i++)
                {
                    c[i] = pow(a[i],b[i]);
                }
            }
        }
    }

    cudaDeviceSynchronize();

    cudaProfilerStop();

    delete[] a;
    delete[] b;
    delete[] c;

    cudaFree((void*)d_a);
    cudaFree((void*)d_b);
    cudaFree((void*)d_c);
}
#包括
#包括
#包括
#根据块64定义线程
__全局无效k_dummykernel1(常量浮点*a,常量浮点*b,浮点*c,常量整数N)
{
const int id=blockIdx.x*blockDim.x+threadIdx.x;
if(id对于(int k=0;k我没有得到像您这样极端的结果,因此我不确定这是否真的会对您有所帮助。我看到第二个线程的API调用速度较慢,因此确保只有一个线程处理所有CUDA API调用会在一定程度上改善结果。这通常是一个好主意,并且您可以看到,在小节中没有这种情况。简单的方法是:

#pragma omp parallel num_threads(2)
{
    for(int k = 0; k <= KMAX; k++)
    {
        if (omp_get_thread_num() == 0)
        {
            k_dummykernel1<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
            k_dummykernel2<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
            k_dummykernel3<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
            k_dummykernel4<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);

            k_dummykernel1<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
            k_dummykernel2<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
            k_dummykernel3<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
            k_dummykernel4<<<(N + THREADS_PER_BLOCK - 1)/THREADS_PER_BLOCK, THREADS_PER_BLOCK>>>(d_a,d_b,d_c,N);
        }
        else
        {
            for(int i = 0; i < N2; i++)
            {
                c[i] = pow(a[i],b[i]);
            }
        }
        // this makes sure that the behavior is consistent
        #pragma omp barrier
    }
}
使用
nvprof
我得到:

Serial time:                   0.058805
Parallel time (pinned thread): 0.054116
Parallel time (sections):      0.053535

因此,基本上你必须用大量的盐从可视化分析器中获取结果。从详细跟踪中获得的洞察力通常非常有用,但在这种情况下,你应该依赖于端到端的测量。

我已经运行了你的代码,并在可视化分析器中查看了它。我看到的与你看到的相反。kerne的束在VisualProfiler中,左侧的内核启动更分散,在时间轴上花费的时间更长,右侧的内核启动束更紧密,在时间轴上花费的时间更少。这是在CUDA 9上。2@RobertCrovella我也在使用CUDA 9.2。您是否使用了其他编译器开关?或者您是否修改了一些探查器中的设置?我在Ubuntu 18.04上运行这段代码,GPU:GTX 1080 TI,CPU:Intel Xeon W-2133Thanks!它实际上有助于将k迭代次数增加到2000次。这有点奇怪,但还好,只要基本思想可行,我就可以使用它。如果你能再回答我两个问题就太好了:1.在你的示例代码中,可能吗简单地在CPU-i循环前面放一个“#pragma omp parallel for”,进一步并行化这个部分?或者我必须增加外部pragma中的num#u threads(…)?2.如何测量计时?我总是使用std::chrono,并在第二次迭代开始时获取开始时间戳(因为有人告诉我,第一个总是慢得多)和cudaDeviceSyncronize()之后的结束时间戳不幸的是,没有,您需要在团队的所有线程中遇到for循环,但至少有一个线程正在处理内核。如果您想进一步并行化内部循环,可能需要嵌套的并行区域,这可能会变得混乱。或者您可以在考虑到少一个线程的情况下对循环应用手动工作共享。2)我在k循环中使用了
clock\u gettime
(不包括计时中的
cudaDeviceSyncronize
)。
Serial time:                   0.058805
Parallel time (pinned thread): 0.054116
Parallel time (sections):      0.053535