Cuda “全局”函数如何返回值或像C/C++;做

Cuda “全局”函数如何返回值或像C/C++;做,cuda,Cuda,最近我在CUDA上做字符串比较工作,我想知道当一个_全局_函数找到我要查找的确切字符串时,它怎么能返回一个值 我的意思是,我需要包含大量线程的_uglobal _u u u u u u u u u u u u u u u u u u u u u u u u u u u u 我正在使用CUDA C。我怎么可能做到这一点呢?全局函数并不像您想象的那样包含大量线程。它只是一个运行在设备上的内核函数,通过传递指定线程模型的参数来调用。CUDA采用的模型是二维网格模型,然后是网格上每个块内部的三维线程模型

最近我在CUDA上做字符串比较工作,我想知道当一个_全局_函数找到我要查找的确切字符串时,它怎么能返回一个值

我的意思是,我需要包含大量线程的_uglobal _u u u u u u u u u u u u u u u u u u u u u u u u u u u u


我正在使用CUDA C。我怎么可能做到这一点呢?

全局函数并不像您想象的那样包含大量线程。它只是一个运行在设备上的内核函数,通过传递指定线程模型的参数来调用。CUDA采用的模型是二维网格模型,然后是网格上每个块内部的三维线程模型

对于您遇到的问题类型,除了在每个块中使用1D线程的1D网格之外,实际上不需要使用任何东西,因为字符串池与其他问题(例如矩阵乘法)一样拆分为2D是没有意义的

我将介绍一个简单的示例,例如字符串池中的100个字符串,您希望以并行方式而不是顺序地检查它们

//main
//Should cudamalloc and cudacopy to device up before this code
dim3 dimGrid(10, 1); // 1D grid with 10 blocks
dim3 dimBlocks(10, 1); //1D Blocks with 10 threads 
fun<<<dimGrid, dimBlocks>>>(, Height)
//cudaMemCpy answerIdx back to integer on host

//kernel (Not positive on these types as my CUDA is very rusty
__global__ void fun(char *strings[], char *stringToMatch, int *answerIdx)
{
    int idx = blockIdx.x * 10 + threadIdx.x;

    //Obviously use whatever function you've been using for string comparison
    //I'm just using == for example's sake
    if(strings[idx] == stringToMatch)
    { 
       *answerIdx = idx
    }
} 
//main
//cudamalloc和cudacopy是否应该在该代码之前启动设备
dim3 dimGrid(10,1);//具有10个块的一维网格
dim3 DIM块(10,1)//带10个螺纹的1D块
乐趣(,身高)
//cudaMemCpy将主机上的IDX应答回整数
//内核(在这些类型上不是积极的,因为我的CUDA非常生锈
__全局无效乐趣(char*strings[],char*stringToMatch,int*answerIdx)
{
int idx=blockIdx.x*10+threadIdx.x;
//显然,可以使用任何用于字符串比较的函数
//我只是用==作为例子
if(strings[idx]==stringToMatch)
{ 
*answerIdx=idx
}
} 
这显然不是最有效的方法,也很可能不是传递参数和使用CUDA处理内存的确切方法,但我希望它能够解决工作负载分割的问题,并且“全局”函数可以在许多不同的内核上执行,所以你不能真的告诉它们全部停止通过将工作负载分配到设备上(当然是以合理的方式),您将获得更快的速度将给您带来巨大的性能改进。为了了解线程模型,我强烈建议您阅读Nvidia网站上有关CUDA的文档。这些文档将极大地帮助您,并教您设置网格和块以获得最佳性能的最佳方法。

在CUDA(或Nvidia GPU)中没有任何方法一个线程中断所有正在运行的线程的执行。一旦发现结果,就不能立即退出内核,这在今天是不可能的

但是,在一个线程找到结果后,您可以让所有线程尽快退出

__global___ void kernel(volatile bool *found, ...) 
{
    while (!(*found) && workLeftToDo()) {

       bool iFoundIt = do_some_work(...); // see notes below

       if (iFoundIt) *found = true;
    }
}
关于这一点的一些注释

  • 注意使用
    volatile
    。这很重要
  • 在启动内核之前,请确保初始化
    found
    ——它必须是指向
    false
    的设备指针
  • 当发现另一个线程更新
    时,线程不会立即退出。它们只会在下次返回while循环顶部时退出
  • 如何实现
    做一些工作
    很重要。如果工作量太大(或变量太大),那么在找到结果后退出的延迟将很长(或变量太大)。如果工作量太小,那么线程将花费大部分时间检查
    找到的
    ,而不是做有用的工作
  • dou_some_work
    还负责分配任务(即计算/递增索引),具体操作方式取决于具体问题
  • 如果您启动的块数远远大于当前GPU上内核的最大占用率,并且在第一次运行的线程块“波”中未找到匹配项,则此内核(以及下面的一个)可以死锁。如果在第一波中找到匹配项,则以后的块将仅在
    found==true
    之后运行,这意味着它们将启动,然后立即退出。解决方案是只启动可以同时驻留的尽可能多的块(也称为“最大启动”),并相应地更新任务分配
  • 如果任务数量相对较少,则可以使用
    If
    替换
    while
    ,并运行足够多的线程来覆盖任务数量。这样就不会出现死锁(但前一点的第一部分适用)
  • workleftodo()
    是特定于问题的,但是当没有剩余的工作要做时,它将返回false,以便在没有找到匹配项的情况下不会死锁
  • 现在,上述情况可能会导致过度的分区占用(所有线程都在同一内存上),尤其是在没有一级缓存的旧体系结构上。因此,您可能需要编写一个稍微复杂的版本,使用每个块的共享状态

    __global___ void kernel(volatile bool *found, ...) 
    {
        volatile __shared__ bool someoneFoundIt;
    
        // initialize shared status
        if (threadIdx.x == 0) someoneFoundIt = *found;
        __syncthreads();
    
        while(!someoneFoundIt && workLeftToDo()) {
    
           bool iFoundIt = do_some_work(...); 
    
           // if I found it, tell everyone they can exit
           if (iFoundIt) { someoneFoundIt = true; *found = true; }
    
           // if someone in another block found it, tell 
           // everyone in my block they can exit
           if (threadIdx.x == 0 && *found) someoneFoundIt = true;
    
           __syncthreads();
        }
    }
    
    这样,每个块一个线程轮询全局变量,只有找到匹配项的线程才会写入全局变量,因此全局内存流量最小化

    旁白:_全局__函数是无效的,因为很难定义如何将1000个线程中的值返回到单个CPU线程中。用户在设备或零拷贝内存中设计一个适合其用途的返回数组很简单,但很难创建通用机制


    免责声明:在浏览器中编写的代码,未经测试,未经验证。

    如果您感到冒险,停止内核执行的另一种方法是只执行

    // (write result to memory here)
    __threadfence();
    asm("trap;");
    
    如果找到答案

    这不需要轮询内存,但不如Mark Harris建议的解决方案