三角形区域上的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 */
.....................................