詳しくは「表:四則・絶対値・最小最大値関数」(表B.110「表:四則・絶対値・最小最大値関数」)を参照ください。
OpenCL-Cの整数型はC99と基本的には同じものと考えて構いません。符号付きと符号無しのintとuintがあり。整数をサポートする関数には、プロセッサの演算器の機能であるMAD演算を活用して、本来複数の処理に分ける計算を1つの計算サイクルで行なうことができます。
四則演算(加算、減算、除算、乗算)があり、ビット演算のために2〜3個便利な組込み関数が存在するだけです。
gentype rotate (gentype v, gentype i)
vの各要素に対して、iビットの左シフトを行ないます。左シフトで押し出されたビットは右側に挿入されます。
uint x = 0x12345678; uint shift = 4; output = rotate(x, shift); //(1) printf("rotate: %x\n", output);
4桁左にシフトします。 |
rotate: 23456781
short upsample (char hi, uchar lo) ushort upsample (uchar hi, uchar lo) shortn upsample (charn hi, ucharn lo) ushortn upsample (ucharn hi, ucharn lo) int upsample (short hi, ushort lo) uint upsample (ushort hi, ushort lo) intn upsample (shortn hi, ushortn lo) uintn upsample (ushortn hi, ushortn lo) long upsample (int hi, uint lo) ulong upsample (uint hi, uint lo) longn upsample (intn hi, uintn lo) ulongn upsample (uintn hi, uintn lo)
upsample関数は以下の計算をします。
result[i] = ((short)hi[i] << 8) | lo[i] result[i] = ((ushort)hi[i] << 8) | lo[i] result[i] = ((int)hi[i] << 16) | lo[i] result[i] = ((uint)hi[i] << 16) | lo[i] result[i] = ((long)hi[i] << 32) | lo[i] result[i] = ((ulong)hi[i] << 32) | lo[i]
16ビット型であれば、hiを8ビット左にシフト、32ビットであれば、hiを16ビット左にシフトし、loを下位ビットに設定します。
uint hi = 0x888888; uint lo = 0x222222; ulong output_upsample; output_upsample = upsample(hi,lo); printf("upsample: %lx\n",output_upsample); printf("upsample for mad: %lx\n", upsample(mul_hi(a,b), a*b))
出力.
upsample: 88888800222222 upsample for mad: 10404204001
gentype popcount (gentype x)
引数xの0でないビットの数を計算します。
uint nonzero = 0x00001111; output = popcount(nonzero); //(1) printf("popcount: %x\n", output);
出力.
popcount: 4
gentype clz (gentype x)
clz関数は最上位ビットから連続する0のビット数を計算します。
printf("clz: %u\n", clz(0xf0001111)); //(1) printf("clz: %u\n", clz(0x00000000)); //(2)
出力.
clz: 0 clz: 32
最後に加算をするための関数を2つご案内します。
gentype hadd (gentype x, gentype y)
和のオーバーフローを発生させずに (x + y) >> 1 を計算します。
gentype rhadd (gentype x, gentype y)
和のオーバーフローを発生させずに (x + y + 1) >> 1 を計算します。
output = hadd(a, b); //(1) printf("hadd: %#lx\n", output); output = rhadd(a, b); //(2) printf("rhadd: %#lx\n", output);
出力.
hadd: 0x102001 rhadd: 0x102001
詳しくは「表:乗算関数」(表B.111「表:乗算関数」)を参照ください。
基本的な乗算をより詳しく見てみましょう。例えばOpenCL-Cでuint型間で基本乗算を行なうと以下のような結果となります。
a = 102001 b = 102001 a * b = 4204001
これをulong型にすると計算結果は 10404204001 となります。明らかに桁が少ないことになります。
下記が検証に使ったコードと出力です。
サンプル.
uint a = 0x102001; uint b = 0x102001; uint c = 0; uint output; printf("a = %lx\n", a); printf("b = %lx\n", b); printf("c = %lx\n", c); output = a * b; //(1) printf("a times b = %lx\n", output); ulong a_long = 0x102001; ulong b_long = 0x102001; ulong output_long; output_long = a_long * b_long; //(2) printf("(ulong)a times b = %lx\n", output_long);
出力.
a = 102001 b = 102001 c = 0 a times b = 4204001 (ulong)a times b = 10404204001
もちろんlongを使えば溢れた桁についても結果に参入できますが、OpenCLではメモリのアラインメントが重要な最適化要因となるため、32ビットでの演算が望ましいと言えます。
その答えとしては以下の定義にあるmulやmadといった高速演算関数です。
gentype mul_hi ( gentype x, gentype y)
x * y を計算し、算出した値の上半分のビットを返します。
gentype mul24 ( gentype x, gentype y)
2つの24ビット整数値xとyの積を計算します。xとyは両方とも32ビット整数値ですが計算では下位24ビットのみを使います。xとyの両方が符号つきのときは、 [-223, 223-1] 、符号無しのときは [0, 224-1] の範囲内にあるとき使うことが推奨されています。xとyがこれらの範囲内にないとき、計算結果は実装システムに依存します。
gentype mad24 ( gentype x, gentype y, gentype z)
2つの24ビット整数値xとyの積を計算し、32ビット整数値zを足します。
gentype mad_hi ( gentype a, gentype b, gentype c)
mul_hi(a, b) + c を計算します。
サンプル.
uint a = 0x102001; uint b = 0x102001; uint c = 0; uint output; output = mad24(a, b, c); //(1) printf("mad24: %lx\n", output); output = mul_hi(a, b); //(2) printf("mul_hi: %lx\n", output); output = mad_hi(a, b, c); //(3) printf("mad_hi: %lx\n", output);
出力.
mad24: 4204001 mul_hi: 104 mad_hi: 104
詳しくは「表:(飽和)加算・減算関数」(表B.112「表:(飽和)加算・減算関数」)を参照ください。
飽和演算とは信号処理の際に 0xFFFFFFFF 以上の値となる計算は全て、 0xFFFFFFFF と一意的に処理を行なうような演算のことを指します。
前の項目のサンプルコードで使った、 mad_sat 関数がそれにあたります。以下がOpenCLでサポートしている、整数型の飽和演算です。
gentype mad_sat ( gentype a, gentype b, gentype c)
a * b + c を計算します。結果については飽和を適用します。
gentype add_sat (gentype x, gentype y)
x + y を計算します。結果については飽和を適用します。
uint a = 0x102001; uint b = 0x102001; uint c = 0; uint output; int a_complement = 0x100000; int b_complement = 0x100000; int c_complement = 1111; output = add_sat(a, b); //(1) printf("add_sat: %#lx\n", output); output = mad_sat(a, b, c); //(2) printf("mad_sat: %u\n", output); output = mad_sat(a_complement, b_complement, c_complement); //(3) printf("mad_sat with sign: %d\n", output);
出力.
add_sat: 0x204002 //(1) mad_sat: 4294967295 //(2) mad_sat with sign: 2147483647 //(3)
uintに格納できる整数の範囲は、 0〜4,294,967,295 となります。(2の補数で表す)intに格納できる整数の範囲は、 −2,147,483,648〜2,147,483,647 です。
Copyright 2018-2019, by Masaki Komatsu