8. 第八章 指令集
這一章佔了整個手冊的一大半(百十來頁吧),主要介紹各種指令,雖然頁數很多,但是大多數指令都很簡單。
8.1. 指令的形式和語義描述
這章就是主要描述每個PTX指令。除了指令的形式和語義之外還有一些例子來描述這些指令的使用場景。
8.2. PTX 指令
PTX指令一般有0-4個操作數,外加一個可選的判斷標誌,一般第一個都是目的地址,後面的是源地址,也可以有兩個目的地址,比如:
setp.lt.s32 p|q, a, b; // p = (a < b); q = !(a < b);
8.3. 判斷操作
就和C語言的判斷是一樣的,會返回一個true or false:
if (i < n)
j = j + 1;
setp.lt.s32 p, i, n; // p = (i < n)
@p add.s32 j, j, 1; // if i < n, add 1 to j
還可以有分支:
setp.lt.s32 p, i, n; // compare i to n
@!p bra L1; // if False, branch over
add.s32 j, j, 1;
L1: ...
8.3.1. 比較
8.3.1.1. 整數比較
8.3.1.2. 浮點數比較
下面這個NaN就是不是個數的玩意。
這是另外的一種比較:
8.3.2. 對於判斷值的操作
判斷值(也就是true of false),可以使用or, xor, not, 和mov操作。並沒有在整數和判斷值的轉換操作,但是setp可以從一個整數產生一個判斷值:
selp.u32 %r1,1,0,%p; // convert predicate to 32-bit value
8.4. 指令操作數的類型
指令必須得帶操作數的大小定義,而且一些操作需要多個類型定義:
.reg .u16 a;
.reg .f32 d;
cvt.f32.u16 d, a; // convert 16-bit unsigned to 32-bit floatFacebook
通常,操作數和操作定義的長度可以相互轉化,但是有一定規則:
8.4.1. 操作數超過指令要求的長度
就是當操作數的長度和操作指令長度要求不一致的時候的轉換方法:
這是源操作數:
這是目的操作數:
8.5. 在控制結構中線程的分支
一個CTA裏的線程都是一起執行的,除非它們遇到了一些判斷語句之類的,我們叫這種分開執行爲分歧(divergent),稱一塊執行爲統一(uniform),這兩種情況都很常見。分支使得程序運行低效,應該儘可能快得使得線程統一。因此PTX提供了一個.uni語句用於在確定沒有分支的時候,手動統一線程,以提高程序的運行效率。
8.6. 語義
就是使用C語言對PTX進行描述,除非C語言不能充分描述。
8.6.1. 十六位代碼
GPU使用16位或者32位數據傳輸,如果在32位的情況下,16位的寄存器要被映射到32位的寄存器上,這會導致計算的不同(32位有高位和低位之分)。解決這個有兩種辦法,一個是轉換到32位一種是機器無關的在哪都用16位。
8.7. 指令
8.7.1. 整數運算指令
8.7.1.1. 整數運算指令: add
加法:d = a + b;
//具體使用方法如下
add.type d, a, b;
add{.sat}.s32 d, a, b; // .sat applies only to .s32
//其中.type可以換成下面的這些
.type = { .u16, .u32, .u64,
.s16, .s32, .s64 };
.sat的意思是限制結果的範圍在MININT..MAXINT之間不要溢出,只適合於.s32類型
8.7.1.2. 整數運算指令: sub
減法:d = a - b;
sub.type d, a, b;
sub{.sat}.s32 d, a, b; // .sat applies only to
.s32 .type = { .u16, .u32, .u64,
.s16, .s32, .s64 };
都和.add一樣
8.7.1.3. 整數運算指令: mul
乘法:這裏好像還區分高位低位
t = a * b;
n = bitwidth of type;
d = t; // for .wide
d = t<2n-1..n>; // for .hi variant
d = t<n-1..0>; // for .lo variant
具體例子:
mul.wide.s16 fa,fxs,fys; // 16*16 bits yields 32 bits
mul.lo.s16 fa,fxs,fys; // 16*16 bits, save only the low 16 bits
mul.wide.s32 z,x,y; // 32*32 bits, creates 64 bit result
8.7.1.4. 整數運算指令: mad
乘加:
t = a * b;
n = bitwidth of type;
d = t + c; // for .wide
d = t<2n-1..n> + c; // for .hi variant
d = t<n-1..0> + c; // for .lo variant
這裏也是有lo,hi之分。
//這裏這個@p就是條件語句
@p mad.lo.s32 d,a,b,c;
mad.lo.s32 r,p,q,r;
8.7.1.5. 整數運算指令: mul24
24位整數值的乘法:不太知道這有啥用
t = a * b;
d = t<47..16>; // for .hi variant
d = t<31..0>; // for .lo variant
語法:
mul24{.hi,.lo}.type d, a, b;
.type = { .u32, .s32 };
8.7.1.6. 整數運算指令: mad24
24位整數乘法:
t = a * b;
d = t<47..16> + c; // for .hi variant
d = t<31..0> + c; // for .lo vari
基本和加法沒啥區別:
mad24.lo.s32 d,a,b,c; // low 32-bits of 24x24-bit signed multiply.
8.7.1.7. 整數運算指令: sad
最悲傷指令:d = c + ((a<b) ? b-a : a-b);
sad.type d, a, b, c;
.type = { .u16, .u32, .u64,
.s16, .s32, .s64 };
8.7.1.8. 整數運算指令: div
除法:d = a/b。
div.type d, a, b;
.type = { .u16, .u32, .u64,
.s16, .s32, .s64 };
8.7.1.9. 整數運算指令: rem
取餘:d = a % b;
rem.type d, a, b;
.type = { .u16, .u32, .u64,
.s16, .s32, .s64 };
8.7.1.10. 整數運算指令: abs
取絕對值:d = |a|;
abs.type d, a;
.type = { .s16, .s32, .s64 };
8.7.1.11. 整數運算指令: neg
負數:d = -a;
neg.type d, a;
.type = { .s16, .s32, .s64 };
8.7.1.12. 整數運算指令: min
最小值:d = (a < b) ? a : b; // Integer (signed and unsigned)
min.type d, a, b;
.type = { .u16, .u32, .u64,
.s16, .s32, .s64 };
8.7.1.13. 整數運算指令: max
最大值:d = (a > b) ? a : b; // Integer (signed and unsigned)
max.type d, a, b;
.type = { .u16, .u32, .u64,
.s16, .s32, .s64 };
8.7.1.14. 整數運算指令: popc
算一個數的二進制表示裏有多少個1(這有啥用。。。)
.u32 d = 0;
while (a != 0) {
if (a & 0x1) d++;
a = a >> 1;
}
用起來倒是很簡單:
popc.type d, a;
.type = { .b32, .b64 };
8.7.1.15. 整數運算指令: clz
計算一個數的二進制開頭有多少零(嗯。。。):
.u32 d = 0;
if (.type == .b32) {
max = 32; mask = 0x80000000;
}
else {
max = 64; mask = 0x8000000000000000;
}
while (d < max && (a&mask == 0) ) {
d++;
a = a << 1;
}
語法:
clz.type d, a;
.type = { .b32, .b64 };
8.7.1.16. 整數運算指令: bfind
這個是返回最高有效位的位置:
//講真,每太看懂這解釋
msb = (.type==.u32 || .type==.s32) ? 31:63;
// negate negative signed inputs
if ( (.type==.s32 || .type==.s64) && ( a & (1<<msb))){
a = ~a;
}
.u32 d = 0xffffffff;
for (.s32 i=msb; i>=0; i--) {
if (a & (1<<i)) { d = i; break;}
}
if (.shiftamt && d != 0xffffffff) {d = msb - d; }
使用方法如下:
bfind.u32 d, a;
bfind.shiftamt.s64 cnt, X; // cnt is .u32
8.7.1.17. 整數運算指令: brev
這個比較好理解就是把每一位反轉:
msb = (.type==.b32) ? 31 : 63;
for (i=0; i<=msb; i++) {
d[i] = a[msb-i];
}
語法:
brev.type d, a;
.type = { .b32, .b64 };
8.7.1.18. 整數運算指令: bfe
提取一個數的某一段二進制:
msb = (.type==.u32 || .type==.s32) ? 31 : 63;
pos = b & 0xff; // pos restricted to 0..255 range
len = c & 0xff; // len restricted to 0..255 range
if (.type==.u32 || .type==.u64 || len==0)
sbit = 0;
else
sbit = a[min(pos+len-1,msb)];
d = 0;
for (i=0; i<=msb; i++) {
d[i] = (i<len && pos+i<=msb) ? a[pos+i] : sbit;
}
反正,參數的意思是這樣的:
bfe.b32 d,a,start,len;
從start開始取a的len位的數,賦值給d。
8.7.1.19. 整數運算指令: bfi
這個和上面那個異曲同工啊,是把某個數插入到另外一個數裏:
msb = (.type==.b32) ? 31 : 63;
pos = c & 0xff; // pos restricted to 0..255 range
len = d & 0xff; // len restricted to 0..255 range
f = b;
for (i=0; i<len && pos+i<=msb; i++) {
f[pos+i] = a[i];
}
語法:
bfi.b32 d,a,b,start,len;
是把a插入到b的從start開始的len位,然後賦值給d。
8.7.1.20. 整數運算指令: dp4a
四路點積和:
d = c;
//Extract 4 bytes from a 32bit input and sign or zero extend based on input type.
Va = extractAndSignOrZeroExt_4(a, .atype);
Vb = extractAndSignOrZeroExt_4(b, .btype);
for (i = 0; i < 4; ++i) {
d += Va[i] * Vb[i];
}
不太明白這和普通點積是啥子區別:
dp4a.u32.s32 d1, a1, b1, c1;
另外主要這個只能在計算能力6.1或以上的機器上使用。
8.7.1.21. 整數運算指令: dp2a
和上面那個差不多,不過還是不太明白是搞啥的:
d = c;
// Extract two 16-bit values from a 32-bit input and sign or zero extend based on input type.
Va = extractAndSignOrZeroExt_2(a, .atype);
// Extract four 8-bit values from a 32-bit input and sign or zer extend
// based on input type.
Vb = extractAndSignOrZeroExt_4(b, .btype);
b_select = (.mode == .lo) ? 0 : 2;
for (i = 0; i < 2; ++i) {
d += Va[i] * Vb[b_select + i];
}
舉例:
dp2a.lo.u32.u32 d0, a0, b0, c0;
dp2a.hi.u32.s32 d1, a1, b1, c1;
8.7.2. 長精度整數運算指令
8.7.2.1. 長精度運算指令: add.cc
這種是可以獲得進位的加法,進位被寫到CC.CF(這大概是個寄存器吧)。
語法:
add.cc.type d, a, b;
.type = { .u32, .s32, .u64, .s64 };
8.7.2.2. 長精度運算指令: addc
這個是將進位加上:d = a + b + CC.CF;
語法:
addc{.cc}.type d, a, b;
.type = { .u32, .s32, .u64, .s64 };
8.7.2.3. 長精度運算指令: sub.cc
這種是可以獲得借位的減法,借位被寫到CC.CF(這大概是個寄存器吧)。
語法:
sub.cc.type d, a, b;
.type = { .u32, .s32, .u64, .s64 };
8.7.2.4. 長精度運算指令: subc
這個是將進位加上:d = a - (b + CC.CF);
語法:
subc{.cc}.type d, a, b;
.type = { .u32, .s32, .u64, .s64 };
8.7.2.5. 長精度運算指令: mad.cc
分高低位的乘加運算:
t = a * b;
d = t<63..32> + c; // for .hi variant
d = t<31..0> + c; // for .lo variant
語法:
mad{.hi,.lo}.cc.type d, a, b, c;
.type = { .u32, .s32, .u64, .s64 };
8.7.2.6. 長精度運算指令: madc
帶精度的高地位運算:
t = a * b;
d = t<63..32> + c + CC.CF; // for .hi variant
d = t<31..0> + c + CC.CF; // for .lo variant
語法:
madc{.hi,.lo}{.cc}.type d, a, b, c;
.type = { .u32, .s32, .u64, .s64 };
8.7.3. 浮點數運算指令
.ftz通過把非格式化浮點數的輸入和結果沖洗成設備無關的保號零來保證後向兼容sm_1x設備。(所以到底是個蛇。。。)
8.7.3.1. 浮點數運算指令: testp
檢測浮點數的性質:
testp.op.type p, a; // result is .pred
.op = { .finite, .infinite,
.number, .notanumber,
.normal, .subnormal };
.type = { .f32, .f64 };
附上NaN的wiki解釋
具體解釋:
testp檢測浮點數的設置,會返回true of false
- testp.finite
如果輸入不是無窮大或者NaN返回true. - testp.infinite
如果是正負無窮返回true - testp.number
輸入不是NaN返回true - testp.notanumber
輸入是NaN返回true - testp.normal、
輸入是個格式化浮點數(不是NaN,不是無限大) - testp.subnormal
輸入是個非格式化浮點數(subnormal number)(不是NaN,不是無限大)
8.7.3.2. 浮點數運算指令: copysign
把一個輸入數的符號拷貝給另一個:
//把a的符號拷貝給b然後用d返回
copysign.type d, a, b;
.type = { .f32, .f64 };
8.7.3.3. 浮點數運算指令: add
加法:
add{.rnd}{.ftz}{.sat}.f32 d, a, b;
add{.rnd}.f64 d, a, b;
.rnd = { .rn, .rz, .rm, .rp };
這其中,舍入規則爲:
- .rn
mantissa LSB rounds to nearest even
- .rz
最低有效位變成0
- .rm
最低有效位變成負無窮
- .rp
最低有效位變成正無窮
8.7.3.4. 浮點數運算指令: sub
減法:
sub{.rnd}{.ftz}{.sat}.f32 d, a, b;
sub{.rnd}.f64 d, a, b;
.rnd = { .rn, .rz, .rm, .rp };
8.7.3.5. 浮點數運算指令: mul
乘法:
mul{.rnd}{.ftz}{.sat}.f32 d, a, b;
mul{.rnd}.f64 d, a, b;
.rnd = { .rn, .rz, .rm, .rp };
8.7.3.6. 浮點數運算指令: fma
乘加運算(人家有個很強的名字叫積和熔加運算):
fma.rnd{.ftz}{.sat}.f32 d, a, b, c;
fma.rnd.f64 d, a, b, c;
.rnd = { .rn, .rz, .rm, .rp };
就是d = a*b + c;
8.7.3.7. 浮點數運算指令: mad
這個和上邊那個是一樣的,不知爲啥搞出倆來:
mad{.ftz}{.sat}.f32 d, a, b, c; // .target sm_1x
mad.rnd{.ftz}{.sat}.f32 d, a, b, c; // .target sm_20
mad.rnd.f64 d, a, b, c; // .target sm_13 and higher
.rnd = { .rn, .rz, .rm, .rp };
8.7.3.8. 浮點數運算指令: div
除法:
div.approx{.ftz}.f32 d, a, b; // fast, approximate divide
div.full{.ftz}.f32 d, a, b; // full-range approximate
divide div.rnd{.ftz}.f32 d, a, b; // IEEE 754 compliant
rounding div.rnd.f64 d, a, b; // IEEE 754 compliant rounding
.rnd = { .rn, .rz, .rm, .rp };
8.7.3.9. 浮點數運算指令: abs
絕對值:
abs{.ftz}.f32 d, a;
abs.f64 d, a;
8.7.3.10. 浮點數運算指令: neg
相反數:
neg{.ftz}.f32 d, a;
neg.f64 d, a;
語義:d = -a;
8.7.3.11. 浮點數運算指令: min
取兩個數的最小值:
min{.ftz}.f32 d, a, b;
min.f64 d, a, b;
語義:
if (isNaN(a) && isNaN(b)) d = NaN;
else if (isNaN(a)) d = b;
else if (isNaN(b)) d = a;
else d = (a < b) ? a : b;
8.7.3.12. 浮點數運算指令: max
取最大:
max{.ftz}.f32 d, a, b;
max.f64 d, a, b;
語義:
if (isNaN(a) && isNaN(b)) d = NaN;
else if (isNaN(a)) d = b;
else if (isNaN(b)) d = a;
else d = (a > b) ? a : b;
8.7.3.13. 浮點數運算指令: rcp
取倒數:
rcp.approx{.ftz}.f32 d, a; // fast, approximate reciprocal
rcp.rnd{.ftz}.f32 d, a; // IEEE 754 compliant rounding
rcp.rnd.f64 d, a; // IEEE 754 compliant rounding
.rnd = { .rn, .rz, .rm, .rp };
下圖是關於倒數的規則:
8.7.3.14. 浮點數運算指令: rcp.approx.ftz.f6
這個就是算倒數:rcp.approx.ftz.f64 d, a;
然後下面是倒數表:
8.7.3.15. 浮點數運算指令: sqrt
開平方:
sqrt.approx{.ftz}.f32 d, a; // fast, approximate square root
sqrt.rnd{.ftz}.f32 d, a; // IEEE 754 compliant rounding
sqrt.rnd.f64 d, a; // IEEE 754 compliant rounding
.rnd = { .rn, .rz, .rm, .rp };
還有開平方表:
8.7.3.16. 浮點數運算指令: rsqrt
開平方的倒數:
rsqrt.approx{.ftz}.f32 d, a;
rsqrt.approx.f64 d, a;
8.7.3.17. 浮點數運算指令: rsqrt.approx.ftz.f64
雙精度開平方的倒數(真-精確):rsqrt.approx.ftz.f64 d, a;
8.7.3.18. 浮點數運算指令: sin
8.7.3.19. 浮點數運算指令: cos
8.7.3.20. 浮點數運算指令: lg2
8.7.3.21. 浮點數運算指令: ex2
上面四個就是數學函數:
sin.approx{.ftz}.f32 d, a;
cos.approx{.ftz}.f32 d, a;
lg2.approx{.ftz}.f32 d, a;
ex2.approx{.ftz}.f32 d, a;
8.7.4. 半精度 浮點數運算指令
這種半精度的就是把一個數拆成hi和lo運算
8.7.4.1. 半精度 浮點數運算指令: add
加法:
add{.rnd}{.ftz}{.sat}.f16 d, a, b; // d, a, b are 16 bits in size
add{.rnd}{.ftz}{.sat}.f16x2 d, a, b; // d, a, b are 32 bits in size.
.rnd = { .rn };
它的描述是這樣的,和下面那些也差不多:
if (type == f16) {
d = a + b;
} else if (type == f16x2) {
fA[0] = a[0:15];
fA[1] = a[16:31];
fB[0] = b[0:15];
fB[1] = b[16:31];
for (i = 0; i < 2; i++) {
d[i] = fA[i] + fB[i];
}
}
8.7.4.2. 半精度 浮點數運算指令: sub
減法:
sub{.rnd}{.ftz}{.sat}.f16 d, a, b; // d, a, b are 16 bits in size
sub{.rnd}{.ftz}{.sat}.f16x2 d, a, b; // d, a, b are 32 bits in size.
.rnd = { .rn };
8.7.4.3. 半精度 浮點數運算指令: mul
乘法:
mul{.rnd}{.ftz}{.sat}.f16 d, a, b; // d, a, b are 16 bits in size
mul{.rnd}{.ftz}{.sat}.f16x2 d, a, b; // d, a, b are 32 bits in size.
.rnd = { .rn };
8.7.4.4. 半精度 浮點數運算指令: fma
熔石爲甲命令。。。:
fma.rnd{.ftz}{.sat}.f16 d, a, b, c; // d, a, b, c are 16 bits in size
fma.rnd{.ftz}{.sat}.f16x2 d, a, b, c; // d, a, b, c are 32 bits in size.
.rnd = { .rn };
8.7.5. 比較與選擇指令
8.7.5.1. 比較與選擇指令: set
就各種比較,然後返回一個bool值
//沒c的時候就直接做運算
set.CmpOp{.ftz}.dtype.stype d, a, b;
//有c的時候要將結果和c比較之後返回
set.CmpOp.BoolOp{.ftz}.dtype.stype d, a, b, {!}c;
.CmpOp = { eq, ne, lt, gt, ge, lo, ls, hi, hs, equ, neu, ltu, leu, gtu, geu, num, nan };
.BoolOp = { and, or, xor };
.dtype = { .u32, .s32, .f32 };
.stype = { .b16, .b32, .b64, .u16, .u32, .u64, .s16, .s32, .s64, .f32, .f64 };
這裏的具體解釋(代碼比文字好看+1):
t = (a CmpOp b) ? 1 : 0;
if (isFloat(dtype))
d = BoolOp(t, c) ? 1.0f : 0x00000000;
else
d = BoolOp(t, c) ? 0xffffffff : 0x00000000;
8.7.5.2. 比較與選擇指令: setp
比較:
setp.CmpOp{.ftz}.type p[|q], a, b;
setp.CmpOp.BoolOp{.ftz}.type p[|q], a, b, {!}c;
.CmpOp = { eq, ne, lt, gt, ge, lo, ls, hi, hs, equ, neu, ltu, leu, gtu, geu, num, nan };
.BoolOp = { and, or, xor };
.type = { .b16, .b32, .b64, .u16, .u32, .u64, .s16, .s32, .s64, .f32, .f64 };
主要的意思就是:
t = (a CmpOp b) ? 1 : 0;
p = BoolOp(t, c);
q = BoolOp(!t, c);
//examples
setp.lt.and.s32 p|q,a,b,r;
@q setp.eq.u32 p,i,n;
8.7.5.3. 比較與選擇指令: selp
語法:
selp.type d, a, b, c;
.type = { .b16, .b32, .b64, .u16, .u32, .u64, .s16, .s32, .s64, .f32, .f64 };
語義:d = (c == 1) ? a : b;
8.7.5.4. 比較與選擇指令: slct
和上邊那個差不多:
slct.dtype.s32 d, a, b, c;
slct{.ftz}.dtype.f32 d, a, b, c;
.dtype = { .b16, .b32, .b64, .u16, .u32, .u64, .s16, .s32, .s64, .f32, .f64 };
語義:d = (c >= 0) ? a : b;
8.7.6. 半精度比較指令
8.7.6.1 半精度比較指令: set
這個和全精度的區別就是,人家是按照一半一半比的:
set.CmpOp{.ftz}.f16.stype d, a, b;
set.CmpOp.BoolOp{.ftz}.f16.stype d, a, b, {!}c;
set.CmpOp{.ftz}.f16x2.f16x2 d, a, b;
set.CmpOp.BoolOp{.ftz}.f16x2.f16x2 d, a, b, {!}c;
//各種騷操作
.CmpOp = { eq, ne, lt, le, gt, ge, equ, neu, ltu, leu, gtu, geu, num, nan }; .BoolOp = { and, or, xor };
.stype = { .b16, .b32, .b64, .u16, .u32, .u64, .s16, .s32, .s64, .f32, .f64 };
具體使用方法如下:
if (type == .f16) {
t = (a CmpOp b) ? 1 : 0;
d = BoolOp(t, c) ? 1.0 : 0.0;
} else if (type == .f16x2) {
fA[0] = a[0:15];
fA[1] = a[16:31];
fB[0] = b[0:15];
fB[1] = b[16:31];
t[0] = (fA[0] CmpOp fB[0]) ? 1 : 0;
t[1] = (fA[1] CmpOp fB[1]) ? 1 : 0;
for (i = 0; i < 2; i++) {
d[i] = BoolOp(t[i], c) ? 1.0 : 0.0;
}
}
8.7.6.2 半精度比較指令: setp
和上邊那個差不多,就是這裏的返回值是整數bool類型上面是浮點數。就是比較之後的返回值再和另外一個bool值(ptx這裏叫predicate值)比較
setp.CmpOp{.ftz}.f16 p, a, b;
setp.CmpOp.BoolOp{.ftz}.f16 p, a, b, {!}c;
setp.CmpOp{.ftz}.f16x2 p|q, a, b;
setp.CmpOp.BoolOp{.ftz}.f16x2 p|q, a, b, {!}c;
.CmpOp = { eq, ne, lt, le, gt, ge, equ, neu, ltu, leu, gtu, geu, num, nan };
.BoolOp = { and, or, xor };
語法:
if (type == .f16) {
t = (a CmpOp b) ? 1 : 0;
p = BoolOp(t, c);
} else if (type == .f16x2) {
fA[0] = a[0:15];
fA[1] = a[16:31];
fB[0] = b[0:15];
fB[1] = b[16:31];
t[0] = (fA[0] CmpOp fB[0]) ? 1 : 0;
t[1] = (fA[1] CmpOp fB[1]) ? 1 : 0;
p = BoolOp(t[0], c);
q = BoolOp(t[1], c);
}
8.7.7. 邏輯與轉化指令
8.7.7.1. 邏輯與轉化指令: and
按位與:d = a & b;
語法:and.type d, a, b; .type = { .pred, .b16, .b32, .b64 };
8.7.7.2. 邏輯與轉化指令: or
按位或:d = a | b;
語法:or.type d, a, b; .type = { .pred, .b16, .b32, .b64 };
8.7.7.3. 邏輯與轉化指令: xor
按位異或:d = a ^ b;
語法:xor.type d, a, b; .type = { .pred, .b16, .b32, .b64 };
8.7.7.4. 邏輯與轉化指令: not
非:d = ~a;
語法:not.type d, a, b; .type = { .pred, .b16, .b32, .b64 };
8.7.7.5. 邏輯與轉化指令: cnot
那啥:d = (a==0) ? 1 : 0;
語法:cnot.type d, a, b; .type = { .b16, .b32, .b64 };
8.7.7.6. 邏輯與轉化指令: lop3
這個稍微有點複雜,是對三個數進行邏輯操作,命令的格式是這樣的:lop3.b32 d, a, b, c, immLut;
使用這個要召喚一個函數,這個函數比如是這樣的F(a & b & c),就會對a,b,c進行運算,然後得到的結果賦值給immLut,再返回給d就好了。
使用方法:
F = GetFunctionFromTable(immLut); // returns the function corresponding to immLut value
d = F(a, b, c);
8.7.7.7. 邏輯與轉化指令: shf
這個命令就是莫名其妙地移位,不知道實際有什麼作用:
shf.l.mode.b32 d, a, b, c; // left shift
shf.r.mode.b32 d, a, b, c; // right shift
.mode = { .clamp, .wrap };
具體的實現就是這樣的,首先這個模式不知道是啥意思,然後就是取個移位的位數n,然後移位:
//根據模式和來確定要移動的位數
u32 n = (.mode == .clamp) ? min(c, 32) : c & 0x1f;
switch (shf.dir) { // shift concatenation of [b, a]
case shf.l: // extract 32 msbs
u32 d = (b << n) | (a >> (32-n));
case shf.r: // extract 32 lsbs
u32 d = (b << (32-n)) | (a >> n);
}
8.7.7.8. 邏輯與轉化指令: shl
左移:d = a << b;
用法:shl.type d, a, b; .type = { .b16, .b32, .b64 };
8.7.7.9. 邏輯與轉化指令: shr
右移:d = a >> b;
用法:shr.type d, a, b; .type = { .b16, .b32, .b64 };
8.7.8. 數據移動和轉化指令
8.7.8.1. 緩存操作數
Load指令可以把內存中的一個單位的數據讀取出來並裝載到目標地址中
Store指令可以把一個目標地址中的數據讀取出來並存儲到內存中
1. load指令下的操作
- .ca 這個是把數據加載到所有的cache中,但是會造成coherent問題。要做的是將grid中的L1 cache設置爲不可見
- .cg 只用L2 cache
- .cs cache stream中的操作
- .lu 最後一次使用
- .cv 不再cache
2. store指令下的操作
- .wb 回寫模式。L1 L2都寫。
- .cg cache到global
- .cs cache流
- .wt 直寫模式。寫回global和L2 cache。
Write-through(直寫模式)在數據更新時,同時寫入緩存Cache和後端存儲。此模式的優點是操作簡單;缺點是因爲數據修改需要同時寫入存儲,數據寫入速度較慢。
Write-back(回寫模式)在數據更新時只寫入緩存Cache。只在數據被替換出緩存時,被修改的緩存數據纔會被寫到後端存儲。此模式的優點是數據寫入速度快,因爲不需要寫存儲;缺點是一旦更新後的數據未被寫入存儲時出現系統掉電的情況,數據將無法找回。