-1

OpenCL では、コードは次のように記述されます。

void unpack_8bit_to_16bit( const __m128i a, __m128i& b0, __m128i& b1 ) 
{
      __m128i zero = _mm_setzero_si128();
      b0 = _mm_unpacklo_epi8( a, zero );
      b1 = _mm_unpackhi_epi8( a, zero );
}

このコードをC言語に変換したいのですが、可能ですか?

4

1 に答える 1

2

コメントにあるように、これは OpenCL コードではありません。ただし、このコードをOpenCLに変換する方法を意図している場合、ベクトル化へのアプローチは、float4 (4 つの 32 ビット float)、double3 (3 つの 64 ビット double)、long8 (8 つの 64 ビット float) などのベクトル型を使用することです。 integer) など... quad (128 ビット float)、複雑な double などのハードコア型も組み込まれています...

あなたの場合、基本的に必要なのは、入力の低クワッドと高クワッドを分離して、一連のバイトを 16 ビット ワードにアンパックすることです。これは、スウィズリングまたは各ベクトルを明示的に計算することで実行できますが、この特定の計算を実行する別の方法もあります。OpenCL にはベクトル分割メカニズムがあり、任意のベクトル型を 2 つの下位半分と上位半分に分割します。これは次のように行われます。

float4 input = (float4)(4.3, 0.71, 9.1, 44.8);
float2 inputLo = input.lo; // = (4.3, 0.71)
float2 inputHi = input.hi; // = (9.1, 44.8)

キャストまたは明示的に変換します。

これは OpenCL にとって一種の奇妙な問題であることに注意してください。このアンパック メカニズムは、データを SSE レジスタにパックする方法から発生するため、8 ビット要素から 16 ビット要素に切り替える場合は、常にバイトをシャッフルする必要があります。OpenCL では、特定のデータ配置を想定しないベクトル型があるため、これは不要です (また、ある型から別の型に簡単に変換できます)。OpenCL カーネルがたまたま SSE 対応のプロセッサで実行された場合、カーネル コンパイラが自動的にパッキングとアンパッキングを行います。

カーネルは x86 および x64 ハードウェアだけで実行されるわけではなく、GPU、FPGA、およびカスタム チップでも実行されるため、OpenCL で組み込み関数を使用することはできません。その代わりに、カーネルがコンパイルされたプラットフォームで適切な SIMD 命令に自動的に変換される汎用ベクトル型を使用します (実際には、もう少し複雑ですが、それが要点です)。


あなたの最新のコメントを考慮して、これを追加します。組み込み関数を単純な C コードに変換する場合、必要なのは、データが SSE レジスタにどのようにパックされるかを理解することだけです。基本的には、次のように動作します。各 SSE レジスタは 128 ビット幅であるため、16 バイト、8 ワード、4 つの long などを保持できます...これらのタイプを混在させることはできないため、たとえば2 バイトと 7 ワード、各組み込み関数は特定の型を想定しています (たとえば、レジ​​スタ内の各 64 ビット倍精度浮動小数点数の平方根、または各 32 ビット浮動小数点数の平方根が必要な場合があります! どちらの型を選択するかは明らかに重要です)。 .

これらの型は常に連続しているため、8 ワードのベクトルを 2 つの 4 の長さのベクトルに変換したい、つまり「アンパック」して 32 ビットの計算を実行できるようにしたい場合は、次のようにします。

[16-bit][16-bit][16-bit][16-bit][16-bit][16-bit][16-bit][16-bit]

[32-bit][32-bit][32-bit][32-bit] & [32-bit][32-bit][32-bit][32-bit]

2 つの 16 ビット ワードが 1 つの 32 ビット値にマージされてガベージが生成されるため、明らかにレジスタを再利用することはできません。代わりに、各 16 ビット ワードを系統的に取り出し、32 ビット長にキャストし、新しいレジスタに入れる必要があります。SSE はこれをすべてハードウェアで行います (組み込み関数が適切な命令を呼び出します)。

特定のケースでは、16 バイトを含むレジスタがあり、代わりに 8 ワードを含む他の 2 つのレジスタにデータを「出力」したいとします。したがって、入力レジスタに a0..a15 (バイト) が含まれている場合、次のようになります。

b0 = (word)a_0..(word)a_7
b1 = (word)a_8..(word)a_15

配列を使用してCでこれを行うことができ、SSEレジスタを「シミュレート」します(レジスタに収まる可能性のある各ベクトルを含む共用体を使用して派手な方法を実行するか、異なる配列タイプをハードコードして相互に変換することができます)。

参考までに、これを参照してください。少し説明されています (SSE レジスターがどのように機能するかについても読むことをお勧めします。これがパッキングが存在する理由であり、重要な理由です)。

于 2012-08-08T09:04:02.117 に答える