Cuda (默认变体)PTX指令'prmt'何时有用?

Cuda (默认变体)PTX指令'prmt'何时有用?,cuda,operators,nvidia,ptx,Cuda,Operators,Nvidia,Ptx,PTX有多种变体。此问题涉及默认问题,如果将其格式化为C/C++函数,则如下所示: uint32_t prmt(uint32_t a, uint32_t b, uint32_t byte_selectors); 这就是它的作用(改编自官方文件): 在一般形式(未指定模式)中,字节选择器由四个4位选择值组成。两个源参数a和b中的字节从0到7进行编号:{b,a}={{b7,b6,b5,b4},{b3,b2,b1,b0}。对于函数输出中的每个字节,定义一个4位选择值 选择值的3个LSB指定8个源字节

PTX有多种变体。此问题涉及默认问题,如果将其格式化为C/C++函数,则如下所示:

uint32_t prmt(uint32_t a, uint32_t b, uint32_t byte_selectors);
这就是它的作用(改编自官方文件):

在一般形式(未指定模式)中,
字节选择器
由四个4位选择值组成。两个源参数
a
b
中的字节从0到7进行编号:{b,a}={{b7,b6,b5,b4},{b3,b2,b1,b0}。对于函数输出中的每个字节,定义一个4位选择值

选择值的3个LSB指定8个源字节中的哪一个应移动到目标位置。msb定义是否应复制字节值,或者是否应在目标位置的所有8位上复制符号(字节的msb)(字节值的符号扩展);msb=0表示复制文本值;msb=1表示复制符号


我的问题是:这种手术什么时候有用?什么样的计算可以利用它?

PTX指令
prmt
公开了机器指令
prmt
的功能。当未指定任何特殊模式
.f4e、.b4e、.rc8、.ecl、.ecr、.rc16
时,使用
prmt
指令的默认模式

默认模式有两个每字节子模式,由8个源字节中每个字节的4位选择器字段的最高有效位控制。常用的子模式是将选择器字段的msb设为零,这意味着从指定的源字节逐字复制目标字节。此子模式通过设备固有函数
\uuu byte\u perm()
公开,通常用于提取、插入和置换字节或执行8的倍数的位移位。示例用法见

另一个子模式是特殊的,它不复制整个源字节,而是跨目标字节复制指定源字节的最高有效位。为此,选择器字段的msb需要设置为1。程序员必须使用PTX内联汇编来访问此功能

我没有设计GPU硬件,因此无法解释为什么要实现该子模式。当每个字节的msb用作需要转换为整个字节掩码的布尔值时,它通常很有用。这对于32位寄存器中的字节处理通常很有用。请注意,CUDA包含许多用于此类处理的设备函数内部函数,反汇编将确认
prmt
默认模式的msb复制子模式用于其中的许多

下面是一个完整的示例,
paddsb
操作的仿真(带符号饱和的字节加法)。请注意,
prmt
masked\u-sign\u-to\u-byte\u-mask()内使用msb复制

#包括
#包括
#包括
#如果
#定义主机主机主机__
#定义\uuuu设备\uuuu设备__
#else/\uuuuu CUDACC__
#定义主机__
#定义设备__
#endif/\uuuu CUDACC__
#定义MSB_掩码(0x80U)//每个字节的MSB掩码
//r=(a^b)和~c
__主机设备线路图14(线路图32线路图a、线路图32线路图b、线路图32线路图c)
{
uint32_t r;
#如果(\uuuu CUDA\u ARCH\uuuuu>=500)
asm(“lop3.b32%0、%1、%2、%3,0x14;\n\t”:“=r”(r):“r”(a),“r”(b),“r”(c));
#else/\uuuu CUDA\uArch__
r=(a^b)和~c;
#endif/\uuuu CUDA\u拱门__
返回r;
}
//r=(a^b)和c
__主机设备故障诊断仪28(故障诊断仪a、故障诊断仪b、故障诊断仪c)
{
uint32_t r;
#如果(\uuuu CUDA\u ARCH\uuuuu>=500)
asm(“lop3.b32%0、%1、%2、%3,0x28;\n\t”:“=r”(r):“r”(a),“r”(b),“r”(c));
#else/\uuuu CUDA\uArch__
r=(a^b)和c;
#endif/\uuuu CUDA\u拱门__
返回r;
}
//r=a^(~b&c)
__主机uuuuuuuuuuu设备uuuuuu32_uut lop3_d2(uint32_ut a、uint32_ut b、uint32_ut c)
{
uint32_t r;
#如果(\uuuu CUDA\u ARCH\uuuuu>=500)
asm(“lop3.b32%0、%1、%2、%3,0xd2;\n\t”:“=r”(r):“r”(a),“r”(b),“r”(c));
#else/\uuuu CUDA\uArch__
r=a^(~b&c);
#endif/\uu CUDA\u ARCH\uu
返回r;
}
//r=(a&c)|(b&c)
__主机uuuuuuuuuuu设备uuuuuuu32_uut lop3_f4(uint32_ut a、uint32_ut b、uint32_ut c)
{
uint32_t r;
#如果(\uuuu CUDA\u ARCH\uuuuu>=500)
asm(“lop3.b32%0、%1、%2、%3,0xf4;\n\t:“=r”(r):“r”(a),“r”(b),“r”(c));
#else/\uuuu CUDA\uArch__
r=(a&c)|(b&c);
#endif/\uuuu CUDA\u拱门__
返回r;
} 
__主机\uuuuuuuuuu设备\uuuuuuuu32\u屏蔽\u符号\u到字节\u屏蔽(uint32\u a)
{
#如果(\uuuu CUDA\u ARCH\uuuuu>=200)
asm(“prmt.b32%0,%0,0,0xba98;”:“+r”(a));//将MSB转换为掩码
#否则
a=a&MSB\u掩码;
a=a+a-(a>>7);//将MSB扩展到完整字节以创建掩码
#恩迪夫
返回a;
}
__主机设备屏蔽选择(uint32屏蔽a、uint32屏蔽b、uint32屏蔽m)
{
#如果(\uuuu CUDA\u ARCH\uuuuu>=500)
返回lop3_f4(a、b、m);
#elif 0
返回((a)和(m))|((b)和(~(m)));
#否则
报税表(((((一)(二))及(三)(二));;
#恩迪夫
}
/* 
my_paddsb()执行带符号饱和的字节加法。在
在溢出的情况下,阳性结果被钳制在127,而阴性结果被钳制在127
结果被限制在-128。
*/
__主机uuuuuuuuu设备uuuuu32_ut我的paddsb(uint32_ut a,uint32_ut b)
{
uint32总和、res、ofl、sga、msk;
res=(a&~MSB_掩码)+(b&~MSB_掩码);
总和=a^b;
ofl=lop3_14(res,a,sum);//ofl=(res^a)&~sum
sga=屏蔽符号到字节屏蔽(a);//符号(a)-屏蔽
msk=masked_sign_to_byte_mask(ofl);//溢出掩码
res=lop3\u d2(res,~MSB\u MASK,sum);//res=res^(MSB\u MASK&sum)
sga=lop3_28(sga,~MSB_掩码,msk);//sga=(sga^~MSB_掩码)&msk
res=masked_select(sga,res,msk);//res=(sga&msk)|(res&msk)
返回res;
}
__全局无效内核(uint32\u t a、uint32\u t b)
{
printf(“GPU:%08x\n”,my_paddsb(a,b));
}
内部主(空)
{
uint32_t a=0x12ef70a0;
uint32_t b=0x34cd6090;
核(a,b);
cudaDeviceSynchronize();
printf(“CPU:%08x\n”,my_paddsb(a,b));
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#if (__CUDACC__)
#define __HOST__ __host__
#define __DEVICE__ __device__
#else // __CUDACC__
#define __HOST__
#define __DEVICE__
#endif // __CUDACC__

#define MSB_MASK (0x80808080U)  // mask for msb of each byte

// r = (a ^ b) & ~c
__HOST__ __DEVICE__ uint32_t lop3_14 (uint32_t a, uint32_t b, uint32_t c)
{
    uint32_t r;
#if (__CUDA_ARCH__ >= 500)
    asm ("lop3.b32 %0,%1,%2,%3,0x14;\n\t" : "=r"(r) : "r"(a), "r"(b), "r"(c));
#else // __CUDA_ARCH__
    r = (a ^ b) & ~c;
#endif // __CUDA_ARCH__
    return r;
}

// r = (a ^ b) & c
__HOST__ __DEVICE__ uint32_t lop3_28 (uint32_t a, uint32_t b, uint32_t c)
{
    uint32_t r;
#if (__CUDA_ARCH__ >= 500)
    asm ("lop3.b32 %0,%1,%2,%3,0x28;\n\t" : "=r"(r) : "r"(a), "r"(b), "r"(c));
#else // __CUDA_ARCH__
    r = (a ^ b) & c;
#endif // __CUDA_ARCH__
    return r;
}

// r = a ^ (~b & c)
__HOST__ __DEVICE__ uint32_t lop3_d2 (uint32_t a, uint32_t b, uint32_t c)
{
    uint32_t r;
#if (__CUDA_ARCH__ >= 500)
    asm ("lop3.b32 %0,%1,%2,%3,0xd2;\n\t" : "=r"(r) : "r"(a), "r"(b), "r"(c));
#else // __CUDA_ARCH__
    r = a ^ (~b & c);
#endif // __CUDA_ARCH__ 
    return r;
}

// r = (a & c) | (b & ~c)
__HOST__ __DEVICE__ uint32_t lop3_f4 (uint32_t a, uint32_t b, uint32_t c)
{
    uint32_t r;
#if (__CUDA_ARCH__ >= 500)
    asm ("lop3.b32 %0,%1,%2,%3,0xf4;\n\t" : "=r"(r) : "r"(a), "r"(b), "r"(c));
#else // __CUDA_ARCH__
    r = (a & c) | (b & ~c);
#endif // __CUDA_ARCH__
    return r;
} 

__HOST__ __DEVICE__ uint32_t masked_sign_to_byte_mask (uint32_t a)
{
#if (__CUDA_ARCH__ >= 200)
    asm ("prmt.b32 %0,%0,0,0xba98;" : "+r"(a)); // convert MSBs to masks
#else
    a = a & MSB_MASK;
    a = a + a - (a >> 7); // extend MSBs to full byte to create mask
#endif
    return a;
}

__HOST__ __DEVICE__ uint32_t masked_select (uint32_t a, uint32_t b, uint32_t m)
{
#if (__CUDA_ARCH__ >= 500) 
    return lop3_f4 (a, b, m);
#elif 0
    return (((a)&(m))|((b)&(~(m))));
#else
    return((((a)^(b))&(m))^(b));
#endif
}

/* 
   my_paddsb() performs byte-wise addition with signed saturation. In the 
   case of overflow, positive results are clamped at 127, while negative 
   results are clamped at -128.
*/
__HOST__ __DEVICE__ uint32_t my_paddsb (uint32_t a, uint32_t b)
{
    uint32_t sum, res, ofl, sga, msk;
    res = (a & ~MSB_MASK) + (b & ~MSB_MASK);
    sum = a ^ b;
    ofl = lop3_14 (res, a, sum); // ofl = (res ^ a) & ~sum
    sga = masked_sign_to_byte_mask (a);  // sign(a)-mask
    msk = masked_sign_to_byte_mask (ofl);// overflow-mask
    res = lop3_d2 (res, ~MSB_MASK, sum); // res = res ^ (MSB_MASK & sum)
    sga = lop3_28 (sga, ~MSB_MASK, msk); // sga = (sga ^ ~MSB_MASK) & msk
    res = masked_select (sga, res, msk); // res = (sga & msk) | (res & ~msk)
    return res;
}

__global__ void kernel (uint32_t a, uint32_t b)
{
    printf ("GPU: %08x\n", my_paddsb (a, b));
}

int main (void)
{
    uint32_t a = 0x12ef70a0;
    uint32_t b = 0x34cd6090;
    kernel<<<1,1>>>(a, b);
    cudaDeviceSynchronize();
    printf ("CPU: %08x\n", my_paddsb (a, b));
    return EXIT_SUCCESS;
}