三角形区域上的CUDA循环展开

三角形区域上的CUDA循环展开,cuda,loop-unrolling,Cuda,Loop Unrolling,是否可以展开三角形区域上的循环,例如: for (int i = 0; i < ROW_LENGTH; i++) { for (int j = 0; j < i; j++) { // Some array operation here } } for(int i=0;i

是否可以展开三角形区域上的循环,例如:

for (int i = 0; i < ROW_LENGTH; i++)
{
    for (int j = 0; j < i; j++)
    {
        // Some array operation here
    }
}
for(int i=0;i

其中ROW_LENGTH是编译时定义的常量?现在,我认为这是不可能的,因为我在程序执行时正在改变(更重要的是,它在编译时不是常量)。我想您可以将2D数组视为1D数组,从0迭代到(行长度^2)/2,然后尝试一些数学技巧来获得索引,但是额外的操作首先会破坏循环展开的目的。

CUDA 7.0编译器将在我的测试中展开此操作。循环索引在编译时都是已知的,所以没有理由不能够

考虑下面的代码,它将a的三角形部分设置为1

#define ROW_LENGTH 4
__global__ void triUnrollTest1(float* a) {
   #pragma unroll
   for (int i = 0; i < ROW_LENGTH; i++)
   {
      #pragma unroll
      for (int j = 0; j < i; j++)
      {
         a[i * ROW_LENGTH + j] = 1.f;
      }
   }
}
使用CUDA 7.0为SM 35编译:
nvcc-arch=sm_35-c triUnroll.cu

然后转储SASS汇编程序:
cuobjdump——转储sass triUnroll.o

我们得到:

code for sm_35
        Function : _Z14triUnrollTest1Pf
.headerflags    @"EF_CUDA_SM35 EF_CUDA_PTX_SM(EF_CUDA_SM35)"
                                                          /* 0x08b8b8a0b010a000 */
/*0008*/                   MOV R1, c[0x0][0x44];          /* 0x64c03c00089c0006 */
/*0010*/                   MOV R0, c[0x0][0x140];         /* 0x64c03c00281c0002 */
/*0018*/                   IADD R2.CC, R0, 0x10;          /* 0xc0840000081c0009 */
/*0020*/                   MOV32I R0, 0x3f800000;         /* 0x741fc000001fc002 */
/*0028*/                   IADD.X R3, RZ, c[0x0][0x144];  /* 0x60804000289ffc0e */
/*0030*/                   ST.E [R2], R0;                 /* 0xe4800000001c0800 */
/*0038*/                   ST.E [R2+0x10], R0;            /* 0xe4800000081c0800 */
                                                          /* 0x080000b810b8b8b8 */
/*0048*/                   ST.E [R2+0x14], R0;            /* 0xe48000000a1c0800 */
/*0050*/                   ST.E [R2+0x20], R0;            /* 0xe4800000101c0800 */
/*0058*/                   ST.E [R2+0x24], R0;            /* 0xe4800000121c0800 */
/*0060*/                   ST.E [R2+0x28], R0;            /* 0xe4800000141c0800 */
/*0068*/                   EXIT;                          /* 0x18000000001c003c */
/*0070*/                   BRA 0x70;                      /* 0x12007ffffc1c003c */
/*0078*/                   NOP;                           /* 0x85800000001c3c02 */
        .....................................


        Function : _Z14triUnrollTest2Pf
.headerflags    @"EF_CUDA_SM35 EF_CUDA_PTX_SM(EF_CUDA_SM35)"
                                                          /* 0x08b8b8a0b010a000 */
/*0008*/                   MOV R1, c[0x0][0x44];          /* 0x64c03c00089c0006 */
/*0010*/                   MOV R0, c[0x0][0x140];         /* 0x64c03c00281c0002 */
/*0018*/                   IADD R2.CC, R0, 0x10;          /* 0xc0840000081c0009 */
/*0020*/                   MOV32I R0, 0x3f800000;         /* 0x741fc000001fc002 */
/*0028*/                   IADD.X R3, RZ, c[0x0][0x144];  /* 0x60804000289ffc0e */
/*0030*/                   ST.E [R2], R0;                 /* 0xe4800000001c0800 */
/*0038*/                   ST.E [R2+0x10], R0;            /* 0xe4800000081c0800 */
                                                          /* 0x080000b810b8b8b8 */
/*0048*/                   ST.E [R2+0x14], R0;            /* 0xe48000000a1c0800 */
/*0050*/                   ST.E [R2+0x20], R0;            /* 0xe4800000101c0800 */
/*0058*/                   ST.E [R2+0x24], R0;            /* 0xe4800000121c0800 */
/*0060*/                   ST.E [R2+0x28], R0;            /* 0xe4800000141c0800 */
/*0068*/                   EXIT;                          /* 0x18000000001c003c */
/*0070*/                   BRA 0x70;                      /* 0x12007ffffc1c003c */
/*0078*/                   NOP;                           /* 0x85800000001c3c02 */
        .....................................

显然,两者都是相同的,并且展开得很好。有趣的是,当我无意中用6.5编译我的第一个答案时,编译器没有展开,所以我想在这种情况下更新是值得的

循环展开的目的是为了在内核中优化代码,还是您正试图将循环转换为执行网格并真正寻找索引规则?可能吗?对对于循环的每次迭代,编译器都知道i和j的值。但这并不意味着编译器会这么做。还值得注意的是,额外的操作可能不会破坏展开点。在许多CUDA应用程序中,算术对运行时没有任何意义。@talonmies我很好奇是否有可能对其进行优化。行长度将在100-300范围内——因此它足够小,内存不成问题,但足够大,我无法手动展开循环。@Jez编译器在运行之前如何知道I和j的值?虽然行长度是一个常量,但i(内部循环的限制)不是,因此它无法优化该循环。除非编译器使用更复杂的方法来预测i和j的值,该方法通过假设确定边界的规则在编译时是可确定的,从而允许变量边界?如果第一个循环展开,则每个展开的迭代都使用显式的
i
。因此,编译器知道所有这些展开迭代中
j
的边界。这允许它在展开的第一个循环中展开第二个循环。
code for sm_35
        Function : _Z14triUnrollTest1Pf
.headerflags    @"EF_CUDA_SM35 EF_CUDA_PTX_SM(EF_CUDA_SM35)"
                                                          /* 0x08b8b8a0b010a000 */
/*0008*/                   MOV R1, c[0x0][0x44];          /* 0x64c03c00089c0006 */
/*0010*/                   MOV R0, c[0x0][0x140];         /* 0x64c03c00281c0002 */
/*0018*/                   IADD R2.CC, R0, 0x10;          /* 0xc0840000081c0009 */
/*0020*/                   MOV32I R0, 0x3f800000;         /* 0x741fc000001fc002 */
/*0028*/                   IADD.X R3, RZ, c[0x0][0x144];  /* 0x60804000289ffc0e */
/*0030*/                   ST.E [R2], R0;                 /* 0xe4800000001c0800 */
/*0038*/                   ST.E [R2+0x10], R0;            /* 0xe4800000081c0800 */
                                                          /* 0x080000b810b8b8b8 */
/*0048*/                   ST.E [R2+0x14], R0;            /* 0xe48000000a1c0800 */
/*0050*/                   ST.E [R2+0x20], R0;            /* 0xe4800000101c0800 */
/*0058*/                   ST.E [R2+0x24], R0;            /* 0xe4800000121c0800 */
/*0060*/                   ST.E [R2+0x28], R0;            /* 0xe4800000141c0800 */
/*0068*/                   EXIT;                          /* 0x18000000001c003c */
/*0070*/                   BRA 0x70;                      /* 0x12007ffffc1c003c */
/*0078*/                   NOP;                           /* 0x85800000001c3c02 */
        .....................................


        Function : _Z14triUnrollTest2Pf
.headerflags    @"EF_CUDA_SM35 EF_CUDA_PTX_SM(EF_CUDA_SM35)"
                                                          /* 0x08b8b8a0b010a000 */
/*0008*/                   MOV R1, c[0x0][0x44];          /* 0x64c03c00089c0006 */
/*0010*/                   MOV R0, c[0x0][0x140];         /* 0x64c03c00281c0002 */
/*0018*/                   IADD R2.CC, R0, 0x10;          /* 0xc0840000081c0009 */
/*0020*/                   MOV32I R0, 0x3f800000;         /* 0x741fc000001fc002 */
/*0028*/                   IADD.X R3, RZ, c[0x0][0x144];  /* 0x60804000289ffc0e */
/*0030*/                   ST.E [R2], R0;                 /* 0xe4800000001c0800 */
/*0038*/                   ST.E [R2+0x10], R0;            /* 0xe4800000081c0800 */
                                                          /* 0x080000b810b8b8b8 */
/*0048*/                   ST.E [R2+0x14], R0;            /* 0xe48000000a1c0800 */
/*0050*/                   ST.E [R2+0x20], R0;            /* 0xe4800000101c0800 */
/*0058*/                   ST.E [R2+0x24], R0;            /* 0xe4800000121c0800 */
/*0060*/                   ST.E [R2+0x28], R0;            /* 0xe4800000141c0800 */
/*0068*/                   EXIT;                          /* 0x18000000001c003c */
/*0070*/                   BRA 0x70;                      /* 0x12007ffffc1c003c */
/*0078*/                   NOP;                           /* 0x85800000001c3c02 */
        .....................................