インテルのみ表示可能 — Ixiasoft
インテルのみ表示可能 — Ixiasoft
5.5.5.7. パイプ呼び出し順序の強制
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーが計算ユニットを生成する際、それぞれが独立している命令のすべてに対し、命令レベルの並列性を構築するわけではありません。そのため、パイプの読み出しおよび書き込み動作は、それらに制御やデータの依存性がない場合でも、互いに独立して実行されない可能性があります。パイプ呼び出しが相互に通信する場合や、パイプが外部デバイスにデータを書き込む場合に、デッドロックが発生する可能性があります。
次のコード例は、producerカーネルとconsumerカーネルで構成されています。パイプc0とc1はバッファーされていないパイプです。c0とc1からのパイプ読み出し動作のスケジュールは、c0とc1へのパイプ書き込み動作と逆の順序になる可能性があります。つまり、producerカーネルが最初にc0に書き込む一方で、consumerカーネルはc1から読み出す可能性があるということです。consumerカーネルが空のパイプから読み出しているため、このパイプ呼び出しにおけるスケジューリングの変更は、デッドロックを発生させる可能性があります。
__kernel void producer (__global const uint * restrict src, const uint iterations, write_only pipe uint __attribute__((blocking)) c0, write_only pipe uint __attribute__((blocking)) c1) { for (int i = 0; i < iterations; i++) { write_pipe (c0, &src[2*i ]); write_pipe (c1, &src[2*i+1]); } } __kernel void consumer (__global uint * restrict dst, const uint iterations, read_only pipe uint __attribute__((blocking)) c0, read_only pipe uint __attribute__((blocking)) c1) { for (int i = 0; i < iterations; i++) { read_pipe (c0, &dst[2*i+1]); read_pipe( c1, &dst[2*i]); } }
__kernel void producer (__global const uint * src, const uint iterations, write_only_pipe uint __attribute__((blocking)) c0, write_only_pipe uint __attribute__((blocking)) c1) { for (int i = 0; i < iterations; i++) { write_pipe(c0, &src[2*i ]); mem_fence(CLK_CHANNEL_MEM_FENCE); write_pipe(c1, &src[2*i+1]); } } __kernel void consumer (__global uint * dst; const uint iterations, read_only_pipe uint __attribute__((blocking)) c0, read_only_pipe uint __attribute__((blocking)) c1) { for(int i = 0; i < iterations; i++) { read_pipe(c0, &dst[2*i ]); mem_fence(CLK_CHANNEL_MEM_FENCE); read_pipe(c1, &dst[2*i+1]); } }
この例では、producerカーネルのmem_fenceは、c0へのパイプ書き込み動作がc1より先に発生するようにしています。同様にconsumerカーネルのmem_fenceは、c0からの読み取り動作がc1より先に行われるようにしています
パイプ使用時におけるカーネル間のメモリーの一貫性の定義
__kernel void producer (__global const uint * restrict src, const uint iterations, write_only pipe uint __attribute__((blocking)) c0, write_only pipe uint __attribute__((blocking)) c1) { for (int i = 0; i < iterations; i++) { write_pipe(c0, &src[2*i]); mem_fence(CLK_CHANNEL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE); write_pipe(c1, &src[2*i+1]); } }
このカーネルでmem_fence関数は、c0への書き込み動作とsrc[2*i] へのメモリーアクセスが、c1への書き込み動作とsrc[2*i+1] へのメモリーアクセスの前に必ず実行されるようにしています。これにより、c0に書き込まれたデータは、c1にデータが書き込まれる前に読み出しパイプから見えるようになります。