6

私はいくつかの CUDA コードを書き始めておりstd::swap()、カーネル内の 2 つの変数に相当することをしたいと考えています。それらはレジスタファイルにあります(流出がない、一部のバッファにないなど)。次のデバイス コードがあるとします。

__device__ foo(/* some args here */) {

    /* etc. */

    int x = /* value v1 */;
    int y = /* value v2 */;

    /* etc. */

    swap(x,y);

    /* etc. */
}        

今、私はただ書くことができました

template <typename T> void swap ( T& a, T& b )
{
  T c(a); a=b; b=c;
}

しかし、私は疑問に思います-この機能のために組み込まれたCUDAはありませんか?

ノート:

  • はい、これをすべてのスレッドで実行したいです。
  • 十分な数のレジスタがあるかどうかは気にしないでください。私がそれらを持っていると仮定します。
4

2 に答える 2

6

次のテストプログラムを検討しました

template <typename T> __device__ void inline swap_test_device1(T& a, T& b)
{
    T c(a); a=b; b=c;
}

template <typename T> __device__ void inline swap_test_device2(T a, T b)
{
    T c(a); a=b; b=c;
}

__global__ void swap_test_global(const int* __restrict__ input1, const int* __restrict__ input2, int* output1, int* output2) {

    int tx = threadIdx.x + blockIdx.x * blockDim.x;

    int x = input1[tx]*input1[tx];
    int y = input2[tx]*input2[tx];

    //swap_test_device2(x,y);
    swap_test_device1(x,y);

    output1[tx] = x;
    output2[tx] = y;

} 

そして私はそれを分解しました。swap_test_device1と を使用した場合の結果swap_test_device2は同じです。一般的な逆アセンブル コードは次のとおりです。

MOV R1, c[0x1][0x100];
S2R R0, SR_CTAID.X;
S2R R2, SR_TID.X;
MOV32I R9, 0x4;  
IMAD R3, R0, c[0x0][0x8], R2;
IMAD R6.CC, R3, R9, c[0x0][0x28];
IMAD.HI.X R7, R3, R9, c[0x0][0x2c];
IMAD R10.CC, R3, R9, c[0x0][0x20];
LD.E R2, [R6];                         loads input1[tx] and stores it in R2
IMAD.HI.X R11, R3, R9, c[0x0][0x24];
IMAD R4.CC, R3, R9, c[0x0][0x30];
LD.E R0, [R10];                        loads input2[tx] and stores it in R0
IMAD.HI.X R5, R3, R9, c[0x0][0x34];
IMAD R8.CC, R3, R9, c[0x0][0x38];
IMAD.HI.X R9, R3, R9, c[0x0][0x3c];
IMUL R2, R2, R2;                       R2 = R2 * R2
ST.E [R4], R2;                         stores input1[tx]*input1[tx] in global memory
IMUL R0, R0, R0;                       R0 = R0 * R0
ST.E [R8], R0;                         stores input2[tx]*input2[tx] in global memory
EXIT ;

逆アセンブルされたコードには明示的なスワップがないようです。つまり、コンパイラは、この単純な例の場合、適切なグローバル メモリ位置にx直接書き込むコードを最適化できます。y

編集

次のより複雑なテストケースを検討しました

__global__ void swap_test_global(const char* __restrict__ input1, const char* __restrict__ input2, char* output1, char* output2) {

    int tx = threadIdx.x + blockIdx.x * blockDim.x;

    char x = input1[tx];
    char y = input2[tx];

    //swap_test_device2(x,y);
    swap_test_device1(x,y);

    output1[tx] = (x >> 3) & y;
    output2[tx] = (y >> 5) & x;

 }

上記の同じ__device__機能を備えています。分解したコードは

MOV R1, c[0x1][0x100];              
S2R R0, SR_CTAID.X;                 
S2R R2, SR_TID.X;           
IMAD R0, R0, c[0x0][0x8], R2;       R0 = threadIdx.x + blockIdx.x * blockDim.x
BFE R7, R0, 0x11f;
IADD R8.CC, R0, c[0x0][0x28];
IADD.X R9, R7, c[0x0][0x2c];
IADD R10.CC, R0, c[0x0][0x20];
LD.E.S8 R4, [R8];                   R4 = x = input1[tx]
IADD.X R11, R7, c[0x0][0x24];
IADD R2.CC, R0, c[0x0][0x30];
LD.E.S8 R5, [R10];                  R5 = y = input2[tx]
IADD.X R3, R7, c[0x0][0x34];
IADD R12.CC, R0, c[0x0][0x38];
IADD.X R13, R7, c[0x0][0x3c];
SHR.U32 R0, R4, 0x3;                R0 = x >> 3
SHR.U32 R6, R5, 0x5;                R6 = y >> 5
LOP.AND R5, R0, R5;                 R5 = (x >> 3) & y
LOP.AND R0, R6, R4;                 R0 = (y >> 5) & x
ST.E.U8 [R2], R5;                   global memory store
ST.E.U8 [R12], R0;                  global memory store
EXIT ;

見てわかるように、明らかなレジスタ スワップはまだありません。

于 2013-10-24T10:22:17.243 に答える
3

私の知る限りでは、これはまったく無関係です。

xyこれらは「実際の」オブジェクトではありません。C++ 標準で記述された抽象マシンにのみ存在します。特に、レジスタには対応していません

プログラムを作成するときにコンパイラがそれらをレジスタに割り当てると想像するかもしれませんが、実際にはそうではありません。レジスタに格納されているものは、シャッフルされたり、複製されたり、別のものに変更されたり、完全に削除されたりする可能性があります。

特に、レジスタに格納されている 2 つの変数を無条件に交換しても、通常はコードがまったく生成されません。その唯一の効果は、コンパイラが、その時点でどのオブジェクトがどのレジスタに格納されているかの内部テーブルを調整することです。

(条件付きスワップの場合でも、通常はコンパイラに任せたほうがよいでしょう)

于 2016-08-05T07:48:51.790 に答える