web-dev-qa-db-ja.com

Cuda gridDimおよびblockDim

blockDimが何であるかはわかりますが、gridDim. Blockdimに問題があり、ブロックのサイズがわかりますが、gridDimとは何ですか?インターネットでは、gridDim.xはx座標のブロック数を示します。

blockDim.x * gridDim.xが提供するものを知るにはどうすればよいですか?

X行にgridDim.x値がいくつあるかを知るにはどうすればよいですか?

たとえば、次のコードを検討してください。

int tid = threadIdx.x + blockIdx.x * blockDim.x;
double temp = a[tid];
tid += blockDim.x * gridDim.x;

while (tid < count)
{
    if (a[tid] > temp)
    {
       temp = a[tid];
    }
    tid += blockDim.x * gridDim.x;
}

tidが0で始まることを知っています。コードにはtid+=blockDim.x * gridDim.xが付きます。この操作後のtidとは何ですか?

40
ehah
  • blockDim.x,y,zは、特定の方向のブロック内のスレッド数を提供します
  • gridDim.x,y,zは、特定の方向のグリッド内のブロック数を提供します
  • blockDim.x * gridDim.xは、グリッド内のスレッドの数を示します(この場合、x方向)

ブロック変数とグリッド変数は、1、2、または3次元にすることができます。 1次元データを処理して1次元ブロックとグリッドのみを作成する場合は、一般的な方法です。

CUDAのドキュメントでは、これらの変数は定義されています here

特に、x次元の合計スレッド(gridDim.x * blockDim.x)がlessである配列のサイズプロセスを実行する場合、ループを作成し、スレッドのグリッドを配列全体に移動させるのが一般的です。この場合、1回のループの繰り返しを処理した後、各スレッドは次の未処理の場所に移動する必要があります。これは、tid+=blockDim.x*gridDim.x;で指定されます。一度にグリッド幅。 「グリッドストライドループ」と呼ばれることもあるこのトピックについては、この ブログ記事 で詳しく説明しています。

NVIDIAのウェビナーページ で利用可能なCUDAの入門ウェビナーをいくつか受講することを検討してください。たとえば、これらの2:

  • CUDA Cを使用したGPUコンピューティング-はじめに(2010)CUDA Cを使用したGPUコンピューティングの基礎の紹介。概念をコードサンプルのウォークスルーで示します。 GPUコンピューティングの経験は不要
  • CUDA Cを使用したGPUコンピューティング-上級1(2010)グローバルメモリ最適化やプロセッサ使用率などの第1レベルの最適化手法。概念は実際のコード例を使用して説明します

これらの概念をよりよく理解したい場合は、2時間もかかります。

グリッドストライドループの一般的なトピックは、詳細に ここ で説明されています。

83
Robert Crovella

CUDAプログラミングガイド からの言い換え:

gridDim:この変数には、グリッドの寸法が含まれます。

blockIdx:この変数には、グリッド内のブロックインデックスが含まれます。

blockDim:この変数には、ブロックの寸法が含まれます。

threadIdx:この変数には、ブロック内のスレッドインデックスが含まれます。

CUDAのスレッド階層について少し混乱しているようです。一言で言えば、カーネルには1つのグリッドがあります(私は常に3次元の立方体として視覚化します)。その各要素はブロックであるため、dim3 grid(10, 10, 2);として宣言されたグリッドは合計10 * 10 * 2ブロックになります。同様に、各ブロックはスレッドの3次元キューブです。

そうは言っても、ブロックとグリッドのx次元のみを使用するのが一般的です。これは、問題のコードが実行しているように見えます。これは、1D配列を使用している場合に特に重要です。その場合、_tid+=blockDim.x * gridDim.x_行は、実際にはグリッド内の各スレッドの一意のインデックスになります。これは、_blockDim.x_が各ブロックのサイズになり、_gridDim.x_がブロックの総数になるためです。

したがって、パラメータを指定してカーネルを起動すると

_dim3 block_dim(128,1,1);
dim3 grid_dim(10,1,1);
kernel<<<grid_dim,block_dim>>>(...);
_

あなたのカーネルには_threadIdx.x + blockIdx.x*blockDim.x_がありました:

_threadIdx.x range from [0 ~ 128)_

_blockIdx.x range from [0 ~ 10)_

_blockDim.x equal to 128_

_gridDim.x equal to 10_

したがって、_threadIdx.x + blockIdx.x*blockDim.x_の計算では、_[0, 128) + 128 * [1, 10)_で定義される範囲内の値が得られます。つまり、tid値の範囲は{0、1、2、...、1279}になります。これは、スレッドをタスクにマップするときに役立ちます。これは、カーネル内のすべてのスレッドに一意の識別子を提供するためです。

ただし、持っている場合

_int tid = threadIdx.x + blockIdx.x * blockDim.x;
tid += blockDim.x * gridDim.x;
_

本質的にはtid = [0, 128) + 128 * [1, 10) + (128 * 10)であり、tid値は{1280、1281、...、2559}の範囲になります。それがどこに関連するかはわかりませんが、それはすべてあなたに依存しますアプリケーションと、スレッドをデータにマップする方法。このマッピングは、カーネルの起動の中心であり、その方法を決定するのはあなたです。カーネルを起動するとき、グリッドとブロックの次元を指定します。そして、あなたはカーネル内のデータへのマッピングを強制しなければなりません。ハードウェアの制限を超えない限り(最新のカードの場合、ブロックごとに最大2 ^ 10スレッド、グリッドごとに2 ^ 16-1ブロックまで使用できます)

46
alrikai

このソースコードでは、4つあり、カーネル関数は10個の配列すべてにアクセスできます。どうやって?

#define N 10 //(33*1024)

__global__ void add(int *c){
    int tid = threadIdx.x + blockIdx.x * gridDim.x;

    if(tid < N)
        c[tid] = 1;

    while( tid < N)
    {
        c[tid] = 1;
        tid += blockDim.x * gridDim.x;
    }
}

int main(void)
{
    int c[N];
    int *dev_c;
    cudaMalloc( (void**)&dev_c, N*sizeof(int) );

    for(int i=0; i<N; ++i)
    {
        c[i] = -1;
    }

    cudaMemcpy(dev_c, c, N*sizeof(int), cudaMemcpyHostToDevice);

    add<<< 2, 2>>>(dev_c);
    cudaMemcpy(c, dev_c, N*sizeof(int), cudaMemcpyDeviceToHost );

    for(int i=0; i< N; ++i)
    {
        printf("c[%d] = %d \n" ,i, c[i] );
    }

    cudaFree( dev_c );
}

10個のスレッドを作成しない理由ex)add<<<2,5>>> or add<5,2>>> Nが10より大きい場合、合理的に少数のスレッドを作成する必要があるため、ex)33 * 1024。

このソースコードは、この場合の例です。配列は10、cudaスレッドは4です。4つのスレッドでのみ10個の配列すべてにアクセスする方法。

cuda詳細のthreadIdx、blockIdx、blockDim、gridDimの意味についてのページを参照してください。

このソースコードでは、

gridDim.x : 2    this means number of block of x

gridDim.y : 1    this means number of block of y

blockDim.x : 2   this means number of thread of x in a block

blockDim.y : 1   this means number of thread of y in a block

2 * 2(ブロック*スレッド)であるため、スレッドの数は4です。

カーネル関数の追加では、スレッドの0、1、2、3インデックスにアクセスできます

-> tid = threadIdx.x + blockIdx.x * blockDim.x

①0+ 0 * 2 = 0

②1+ 0 * 2 = 1

③0+ 1 * 2 = 2

④1+ 1 * 2 = 3

インデックス4、5、6、7、8、9の残りの部分にアクセスする方法whileループに計算があります

tid += blockDim.x + gridDim.x in while

**カーネルの最初の呼び出し**

-1ループ:0 + 2 * 2 = 4

-2ループ:4 + 2 * 2 = 8

-3ループ:8 + 2 * 2 = 12(ただし、この値はfalseですが、外に出ています!)

**カーネルの2回目の呼び出し**

-1ループ:1 + 2 * 2 = 5

-2ループ:5 + 2 * 2 = 9

-3ループ:9 + 2 * 2 = 13(ただし、この値はfalseですが、外に出ている間!)

**カーネルの3番目の呼び出し**

-1ループ:2 + 2 * 2 = 6

-2ループ:6 + 2 * 2 = 10(ただし、この値はfalseですが、外に出ています!)

**カーネルの4番目の呼び出し**

-1ループ:3 + 2 * 2 = 7

-2ループ:7 + 2 * 2 = 11(ただし、この値は偽ですが、外に出ています!)

したがって、0、1、2、3、4、5、6、7、8、9のすべてのインデックスは、tid値でアクセスできます。

このページを参照してください。 http://study.marearts.com/2015/03/to-process-all-arrays-by-reasonably.html 評価が低いため、画像をアップロードできません。

1
Jeonghyun Kim