关于从CUDA中的块到SM的分布的详细信息的问题

时间:2011-08-23 16:17:29

标签: gpgpu nvidia gpu-programming cuda

让我以具有计算能力的硬件1.3为例。

可提供30个SM。然后最多可以同时运行240个块(考虑到寄存器和共享存储器的限制,对块数的限制可能要低得多)。超过240的那些块必须等待可用的硬件资源。

我的问题是何时将超过240的那些块分配给SM。一旦前两个的一些块完成了吗?或者当前240个块的全部完成时?

我写了这样一段代码。

#include<stdio.h>
#include<string.h>
#include<cuda_runtime.h>
#include<cutil_inline.h>

const int BLOCKNUM = 1024;
const int N=240;
__global__ void kernel ( volatile int* mark ) {
    if ( blockIdx.x == 0 ) while ( mark[N] == 0 );
    if ( threadIdx.x == 0 ) mark[blockIdx.x] = 1;
}

int main() {
    int * mark;
    cudaMalloc ( ( void** ) &mark, sizeof ( int ) *BLOCKNUM );
    cudaMemset ( mark, 0, sizeof ( int ) *BLOCKNUM );
    kernel <<< BLOCKNUM, 1>>> ( mark );
    cudaFree ( mark );
    return 0;
}

此代码导致死锁并无法终止。但是如果我将N从240更改为239,则代码可以终止。所以我想知道有关块调度的一些细节。

4 个答案:

答案 0 :(得分:2)

在GT200上,已经通过微基准测试证明,只要SM已经退出运行它的所有当前活动块,就会安排新块。所以答案是当一些块完成时,调度粒度是SM级别。似乎已经达成共识,即费米GPU具有比前几代硬件更精细的调度粒度。

答案 1 :(得分:1)

我找不到任何有关此计算功能的参考资料&lt; 1.3。

Fermi架构引入了一个名为GigaThread引擎的新块调度程序 当一个人完成执行时,GigaThread可以立即替换SM上的块,并且还可以启用并发内核执行。

答案 2 :(得分:0)

虽然没有正式答案,但是当你的积木开始工作和结束时,你可以通过原子操作来衡量。

尝试使用以下代码播放:

#include <stdio.h>

const int maxBlocks=60; //Number of blocks of size 512 threads on current device required to achieve full occupancy

__global__ void emptyKernel() {}


__global__ void myKernel(int *control, int *output) {
        if (threadIdx.x==1) {
                //register that we enter
                int enter=atomicAdd(control,1);
                output[blockIdx.x]=enter;

                //some intensive and long task
                int &var=output[blockIdx.x+gridDim.x]; //var references global memory
                var=1;
                for (int i=0; i<12345678; ++i) {
                        var+=1+tanhf(var);
                }

                //register that we quit
                var=atomicAdd(control,1);
        }
}


int main() {

        int *gpuControl;
        cudaMalloc((void**)&gpuControl, sizeof(int));
        int cpuControl=0;
        cudaMemcpy(gpuControl,&cpuControl,sizeof(int),cudaMemcpyHostToDevice);


        int *gpuOutput;
        cudaMalloc((void**)&gpuOutput, sizeof(int)*maxBlocks*2);
        int cpuOutput[maxBlocks*2];

        for (int i=0; i<maxBlocks*2; ++i) //clear the host array just to be on the safe side
                cpuOutput[i]=-1;

        // play with these values
        const int thr=479;
        const int p=13;
        const int q=maxBlocks;

        //I found that this may actually affect the scheduler! Try with and without this call.
        emptyKernel<<<p,thr>>>();

        cudaEvent_t timerStart;
        cudaEvent_t timerStop;
        cudaEventCreate(&timerStart);
        cudaEventCreate(&timerStop);

        cudaThreadSynchronize();

        cudaEventRecord(timerStart,0);

        myKernel<<<q,512>>>(gpuControl, gpuOutput);

        cudaEventRecord(timerStop,0);
        cudaEventSynchronize(timerStop);

        cudaMemcpy(cpuOutput,gpuOutput,sizeof(int)*maxBlocks*2,cudaMemcpyDeviceToHost);

        cudaThreadSynchronize();
        float thisTime;
        cudaEventElapsedTime(&thisTime,timerStart,timerStop);

        cudaEventDestroy(timerStart);
        cudaEventDestroy(timerStop);
        printf("Elapsed time: %f\n",thisTime);

        for (int i=0; i<q; ++i)
                printf("%d: %d-%d\n",i,cpuOutput[i],cpuOutput[i+q]);
}

您在输出中获得的是块ID,然后输入“time”并退出“time”。通过这种方式,您可以了解这些事件发生的顺序。

答案 3 :(得分:0)

在Fermi上,我确信只要有 room ,就会在SM上安排一个块。即,每当SM完成执行一个块时,如果剩下任何块,它将执行另一个块。 (但是,实际的顺序不是确定性的。)

在旧版本中,我不知道。但您可以使用内置clock()函数验证它。

例如,我使用了以下OpenCL内核代码(您可以轻松地将其转换为CUDA):

   __kernel void test(uint* start, uint* end, float* buffer);
   {
       int id = get_global_id(0);
       start[id] = clock();
       __do_something_here;
       end[id] = clock();
   }

然后将其输出到文件并构建图形。你会看到它的视觉效果。

相关问题