如何为Maxwell和NVIDIA Architecture编写基于LOP3的指令?

时间:2016-05-10 21:57:26

标签: cuda nvidia

Maxwell Architecture根据NVIDIA blog引入了一个名为LOP3的PTX程序集新指令:

  

“执行复杂逻辑操作时可以保存指令   多个输入。“

GTC 2016,一些CUDA开发人员设法通过这些指示加速了Tegra X1处理器(Maxwell)的atan2f功能。

但是,.cu文件中定义的以下函数会导致__SET_LT__LOP3_0xe2的未定义定义。

我是否必须在.ptx文件中定义它们?如果是这样,怎么样?

float atan2f(const float dy, const float dx) 
{
 float flag, z = 0.0f;
 __SET_LT(flag, fabsf(dy), fabsf(dx));

 uint32_t m, t1 = 0x80000000; 
 float t2 = float(M_PI) / 2.0f;

 __LOP3_0x2e(m, __float_as_int(dx), t1, __float_as_int(t2));
 float w = flag * __int_as_float(m) + float(M_PI)/2.0f; 

 float Offset = copysignf(w, dy);
 float t = fminf(fabsf(dx), fabsf(dy)) / fmaxf(fabsf(dx), fabsf(dy));

 uint32_t r, b = __float_as_int(flag) << 2;
 uint32_t mask = __float_as_int(dx) ^ __float_as_int(dy) ^ (~b);
 __LOP3_0xe2(r, mask, t1, __floast_as_int(t));

 const float p = fabsf(__int_as_float(r)) - 1.0f;
 return ((-0.0663f*(-p) + 0.311f) * (-p) + float(float(M_PI)/4.0)) * (*(float *)&r) + Offset;
}

修改

宏定义最终是:

#define __SET_LT(D, A, B) asm("set.lt.f32.f32 %0, %1, %2;" : "=f"(D) : "f"(A), "f"(B))
#define __SET_GT(D, A, B) asm("set.gt.f32.f32 %0, %1, %2;" : "=f"(D) : "f"(A), "f"(B))
#define __LOP3_0x2e(D, A, B, C) asm("lop3.b32 %0, %1, %2, %3, 0x2e;" : "=r"(D) : "r"(A), "r"(B), "r"(C))
#define __LOP3_0xe2(D, A, B, C) asm("lop3.b32 %0, %1, %2, %3, 0xe2;" : "=r"(D) : "r"(A), "r"(B), "r"(C))

1 个答案:

答案 0 :(得分:4)

lop3.b32 PTX instruction可以对3个变量A,B和C执行或多或少的任意布尔(逻辑)操作。

为了设置要执行的实际操作,我们必须提供一个“查找表”立即参数(immLut - 一个8位数量)。如the documentation所示,计算给定操作immLut的必要F(A,B,C)参数的方法是将0xF0的值替换为A,{{ 1}}用于0xCCB用于0xAA在实际所需的等式中。例如,假设我们想要计算:

C

然后我们将通过以下方式计算F = (A || B) && (!C) ((A or B) and (not-C)) 参数:

immLut

请注意,immLut = (0xF0 | 0xCC) & (~0xAA) 的指定等式是一个布尔方程式,将参数FAB视为布尔值,并生成一个真/假结果(C)。但是,计算F的等式是按位逻辑运算。

对于上面的示例,immLut的计算值为 0x54

如果希望在普通的CUDA C / C ++代码中使用PTX指令,最常见的(也可以说是最简单的)方法可能就是使用inline PTX。内联PTX is documented,还有其他问题讨论如何使用它(例如this one),所以我在此不再重复。

以下是上述示例案例的实例。请注意,此特定PTX指令仅适用于cc5.0及更高版本的体系结构,因此请确保至少编译该目标级别。

immLut

由于$ cat t1149.cu #include <stdio.h> const unsigned char A_or_B_and_notC=((0xF0|0xCC)&(~0xAA)); __device__ int my_LOP_0x54(int A, int B, int C){ int temp; asm("lop3.b32 %0, %1, %2, %3, 0x54;" : "=r"(temp) : "r"(A), "r"(B), "r"(C)); return temp; } __global__ void testkernel(){ printf("A=true, B=false, C=true, F=%d\n", my_LOP_0x54(true, false, true)); printf("A=true, B=false, C=false, F=%d\n", my_LOP_0x54(true, false, false)); printf("A=false, B=false, C=false, F=%d\n", my_LOP_0x54(false, false, false)); } int main(){ printf("0x%x\n", A_or_B_and_notC); testkernel<<<1,1>>>(); cudaDeviceSynchronize(); } $ nvcc -arch=sm_50 -o t1149 t1149.cu $ ./t1149 0x54 A=true, B=false, C=true, F=0 A=true, B=false, C=false, F=1 A=false, B=false, C=false, F=0 $ 是PTX代码中的立即常量,我知道使用内联PTX无法将其作为函数参数传递 - 即使使用模板也是如此。根据您的provided link,该演示文稿的作者似乎也使用了一个单独定义的函数来表示特定的期望值 - 在它们的情况下可能是0xE2和0x2E。另外,请注意我已选择编写函数,以便将操作结果作为函数返回值返回。您链接的演示文稿的作者似乎是通过函数参数传回返回值。任何一种方法都应该可行。 (事实上​​,他们似乎已将immLut代码编写为功能而非普通函数。)