为什么是;a=(b>;0)?1:0;优于;如有其他",;CUDA版本?
你能告诉我为什么吗为什么是;a=(b>;0)?1:0;优于;如有其他",;CUDA版本?,cuda,Cuda,你能告诉我为什么吗 a =(b>0)?1:0 比 if (b>0)a=1; else a =0; CUDA版本?请详细说明。非常感谢 Yik < P>不知道CUDA,但在C++和C99中,使用前者可以初始化const变量。< /P> int const a = (b>0) ? 1 : 0; 而对于后者,您不能使成为变量常量,因为您必须在if之前声明它 请注意,它可以写得更短: int const a = (b>0); 你甚至可以去掉括号。。。但我认为这并不能提高
a =(b>0)?1:0
比
if (b>0)a=1; else a =0;
CUDA版本?请详细说明。非常感谢
Yik
< P>不知道CUDA,但在C++和C99中,使用前者可以初始化const变量。< /P>int const a = (b>0) ? 1 : 0;
而对于后者,您不能使成为变量常量,因为您必须在if
之前声明它
请注意,它可以写得更短:
int const a = (b>0);
你甚至可以去掉括号。。。但我认为这并不能提高阅读能力。我发现它更容易阅读。很明显,整个语句的目的是设置a
的值
其目的是将a
赋值给两个值中的一个,而三元条件运算符语法使语句中只有一个a=
我认为标准的if/else都在一行上是丑陋的(不管它是用来做什么的)。一般来说,你需要避免CUDA代码中的分支,否则你可能会得到扭曲发散,这可能会导致巨大的性能损失if
/else
子句通常会根据表达式的测试产生分支。消除分支的一种方法是使用一个表达式,如果编译器足够聪明,则可以在没有分支的情况下实现。这样,英伟达编译器中的所有线程都遵循相同的代码路径。这是一个小测试的结果,以确定是否仍然如此:
__global__ void branchTest0(float *a, float *b, float *d)
{
unsigned int tidx = threadIdx.x + blockDim.x*blockIdx.x;
float aval = a[tidx], bval = b[tidx];
float z0 = (aval > bval) ? aval : bval;
d[tidx] = z0;
}
__global__ void branchTest1(float *a, float *b, float *d)
{
unsigned int tidx = threadIdx.x + blockDim.x*blockIdx.x;
float aval = a[tidx], bval = b[tidx];
float z0;
if (aval > bval) {
z0 = aval;
} else {
z0 = bval;
}
d[tidx] = z0;
}
使用CUDA 4.0发行版编译器编译compute capability 2.0的这两个内核时,比较部分会产生以下结果:
branchTest0:
max.f32 %f3, %f1, %f2;
及
三值运算符被编译成一条浮点最大值指令,而if/then/else被编译成两条指令,一条是比较,另一条是选择。这两个代码都是有条件执行的,都不会产生分支。汇编程序为这些代码发出的机器代码也不同,并且紧密复制了PTX:
branchTest0:
/*0070*/ /*0x00201c00081e0000*/ FMNMX R0, R2, R0, !pt;
及
所以看起来,至少对于具有CUDA 4.0的费米GPU来说,三元运算符产生的指令比等价的if/then/else少。它们之间是否存在性能差异取决于我没有的微基准标记数据 在这两种情况下,编译器将尝试做相同的事情,它将使用谓词执行。您可以在CUDA C编程指南(可通过)和上找到更多信息。本质上,对于这样的短分支,硬件能够为分支的两侧发出指令,并使用谓词指示哪些线程应该实际执行指令
换句话说,性能差异最小。对于较旧的编译器,三级运算符有时会有所帮助,但现在它们是等效的。一般来说,我建议以自然风格编写CUDA代码,并让编译器担心局部分支。除了谓词,GPU硬件还实现“选择”类型指令。使用Talonmes的框架并坚持使用原始海报的代码,我发现使用CUDA 4.0编译器为sm_20生成的两个版本的机器代码是相同的。我使用-keep来保留中间文件,并使用cuobjdump实用程序来生成反汇编。三值运算符和if语句都被转换为FCMP指令,这是一条“select”指令
Talonmes审查的样本案例实际上是一个特殊案例。编译器识别一些常见的源代码习惯用法,例如经常用于表示max()和min()操作的特定三元表达式,并相应地生成代码。等价的if语句不被认为是惯用语
__global__ void branchTest0(float *bp, float *d)
{
unsigned int tidx = threadIdx.x + blockDim.x*blockIdx.x;
float b = bp[tidx];
float a = (b>0)?1:0;
d[tidx] = a;
}
__global__ void branchTest1(float *bp, float *d)
{
unsigned int tidx = threadIdx.x + blockDim.x*blockIdx.x;
float b = bp[tidx];
float a;
if (b>0)a=1; else a =0;
d[tidx] = a;
}
code for sm_20
Function : _Z11branchTest1PfS_
/*0000*/ /*0x00005de428004404*/ MOV R1, c [0x1] [0x100];
/*0008*/ /*0x84009c042c000000*/ S2R R2, SR_Tid_X;
/*0010*/ /*0x94001c042c000000*/ S2R R0, SR_CTAid_X;
/*0018*/ /*0x10019de218000000*/ MOV32I R6, 0x4;
/*0020*/ /*0x20009ca320044000*/ IMAD R2, R0, c [0x0] [0x8], R2;
/*0028*/ /*0x1020dc435000c000*/ IMUL.U32.U32.HI R3, R2, 0x4;
/*0030*/ /*0x80211c03200d8000*/ IMAD.U32.U32 R4.CC, R2, R6, c [0x0] [0x20];
/*0038*/ /*0x90315c4348004000*/ IADD.X R5, R3, c [0x0] [0x24];
/*0040*/ /*0xa0209c03200d8000*/ IMAD.U32.U32 R2.CC, R2, R6, c [0x0] [0x28];
/*0048*/ /*0x00401c8584000000*/ LD.E R0, [R4];
/*0050*/ /*0xb030dc4348004000*/ IADD.X R3, R3, c [0x0] [0x2c];
/*0058*/ /*0x03f01c003d80cfe0*/ FCMP.LEU R0, RZ, 0x3f800, R0;
/*0060*/ /*0x00201c8594000000*/ ST.E [R2], R0;
/*0068*/ /*0x00001de780000000*/ EXIT;
....................................
Function : _Z11branchTest0PfS_
/*0000*/ /*0x00005de428004404*/ MOV R1, c [0x1] [0x100];
/*0008*/ /*0x84009c042c000000*/ S2R R2, SR_Tid_X;
/*0010*/ /*0x94001c042c000000*/ S2R R0, SR_CTAid_X;
/*0018*/ /*0x10019de218000000*/ MOV32I R6, 0x4;
/*0020*/ /*0x20009ca320044000*/ IMAD R2, R0, c [0x0] [0x8], R2;
/*0028*/ /*0x1020dc435000c000*/ IMUL.U32.U32.HI R3, R2, 0x4;
/*0030*/ /*0x80211c03200d8000*/ IMAD.U32.U32 R4.CC, R2, R6, c [0x0] [0x20];
/*0038*/ /*0x90315c4348004000*/ IADD.X R5, R3, c [0x0] [0x24];
/*0040*/ /*0xa0209c03200d8000*/ IMAD.U32.U32 R2.CC, R2, R6, c [0x0] [0x28];
/*0048*/ /*0x00401c8584000000*/ LD.E R0, [R4];
/*0050*/ /*0xb030dc4348004000*/ IADD.X R3, R3, c [0x0] [0x2c];
/*0058*/ /*0x03f01c003d80cfe0*/ FCMP.LEU R0, RZ, 0x3f800, R0;
/*0060*/ /*0x00201c8594000000*/ ST.E [R2], R0;
/*0068*/ /*0x00001de780000000*/ EXIT;
....................................
我收到一位专家的来信,他告诉我“a=(b>0)?1:0;“可以提高CUDA的性能。我很困惑。这与常量无关-这是关于消除SIMT代码中的分支。谢谢。这对于改善外观是有意义的。有人告诉我,它还提高了性能。这不是关于代码的清晰性,而是关于SIMT体系结构(如CUDA)中的性能,在CUDA中,条件块上会出现扭曲差异。没有理由不能获得性能和代码的清晰性(至少在这种情况下)。这种语法对任何有C、Java、JavaScript或C#背景的人来说都很熟悉。@nnnnnn这可能是真的,但这个问题是关于代码效率的,而这并没有回答这个问题。@davidyong-在我发布这个答案时,这个问题没有提到效率,只是问哪个版本“更好”. 两年后,关于效率的部分被编辑成了问题,编辑来自另一个账户。现在它已经引起了我的注意,我正在考虑回滚编辑-在编辑之后使现有答案出错的方式是不合适的。“如果编译器足够聪明”,这是主要的观点。无论如何,编译器应该能够在不进行分支操作的情况下将其编译为。编译器必须知道如何避免分支,并进行相应的优化。您不需要避免所有分支,这只会使代码比需要的复杂得多。您应该对代码有很好的理解,并采取措施尽量减少分歧。统一分支(所有线程采用相同的路由)不会对性能造成影响。短分支可以通过谓词实现,性能影响最小。长发散分支是唯一会招致惩罚的情况——将公共子表达式从分支中提升会有所帮助,因为程序员比编译器更了解如何重构表达式。我同意Tom的观点。“你不需要避开所有的分支
branchTest1:
/*0070*/ /*0x0021dc00220e0000*/ FSETP.GT.AND P0, pt, R2, R0, pt;
/*0078*/ /*0x00201c0420000000*/ SEL R0, R2, R0, P0;
__global__ void branchTest0(float *bp, float *d)
{
unsigned int tidx = threadIdx.x + blockDim.x*blockIdx.x;
float b = bp[tidx];
float a = (b>0)?1:0;
d[tidx] = a;
}
__global__ void branchTest1(float *bp, float *d)
{
unsigned int tidx = threadIdx.x + blockDim.x*blockIdx.x;
float b = bp[tidx];
float a;
if (b>0)a=1; else a =0;
d[tidx] = a;
}
code for sm_20
Function : _Z11branchTest1PfS_
/*0000*/ /*0x00005de428004404*/ MOV R1, c [0x1] [0x100];
/*0008*/ /*0x84009c042c000000*/ S2R R2, SR_Tid_X;
/*0010*/ /*0x94001c042c000000*/ S2R R0, SR_CTAid_X;
/*0018*/ /*0x10019de218000000*/ MOV32I R6, 0x4;
/*0020*/ /*0x20009ca320044000*/ IMAD R2, R0, c [0x0] [0x8], R2;
/*0028*/ /*0x1020dc435000c000*/ IMUL.U32.U32.HI R3, R2, 0x4;
/*0030*/ /*0x80211c03200d8000*/ IMAD.U32.U32 R4.CC, R2, R6, c [0x0] [0x20];
/*0038*/ /*0x90315c4348004000*/ IADD.X R5, R3, c [0x0] [0x24];
/*0040*/ /*0xa0209c03200d8000*/ IMAD.U32.U32 R2.CC, R2, R6, c [0x0] [0x28];
/*0048*/ /*0x00401c8584000000*/ LD.E R0, [R4];
/*0050*/ /*0xb030dc4348004000*/ IADD.X R3, R3, c [0x0] [0x2c];
/*0058*/ /*0x03f01c003d80cfe0*/ FCMP.LEU R0, RZ, 0x3f800, R0;
/*0060*/ /*0x00201c8594000000*/ ST.E [R2], R0;
/*0068*/ /*0x00001de780000000*/ EXIT;
....................................
Function : _Z11branchTest0PfS_
/*0000*/ /*0x00005de428004404*/ MOV R1, c [0x1] [0x100];
/*0008*/ /*0x84009c042c000000*/ S2R R2, SR_Tid_X;
/*0010*/ /*0x94001c042c000000*/ S2R R0, SR_CTAid_X;
/*0018*/ /*0x10019de218000000*/ MOV32I R6, 0x4;
/*0020*/ /*0x20009ca320044000*/ IMAD R2, R0, c [0x0] [0x8], R2;
/*0028*/ /*0x1020dc435000c000*/ IMUL.U32.U32.HI R3, R2, 0x4;
/*0030*/ /*0x80211c03200d8000*/ IMAD.U32.U32 R4.CC, R2, R6, c [0x0] [0x20];
/*0038*/ /*0x90315c4348004000*/ IADD.X R5, R3, c [0x0] [0x24];
/*0040*/ /*0xa0209c03200d8000*/ IMAD.U32.U32 R2.CC, R2, R6, c [0x0] [0x28];
/*0048*/ /*0x00401c8584000000*/ LD.E R0, [R4];
/*0050*/ /*0xb030dc4348004000*/ IADD.X R3, R3, c [0x0] [0x2c];
/*0058*/ /*0x03f01c003d80cfe0*/ FCMP.LEU R0, RZ, 0x3f800, R0;
/*0060*/ /*0x00201c8594000000*/ ST.E [R2], R0;
/*0068*/ /*0x00001de780000000*/ EXIT;
....................................