インテルのみ表示可能 — Ixiasoft
インテルのみ表示可能 — Ixiasoft
5.4.5.8. チャネルの呼び出し順序の強制
計算ユニットを生成する際、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、それぞれが独立している命令のすべてに、命令レベルの並列性を必ず作成するわけではありません。そのため、チャネル読み取りと書き込み動作の間に制御やデータの依存性がない場合でも、それぞれが独立して実行されない場合があります。チャネル呼び出しが相互に通信する際、またはチャネルが外部デバイスにデータを書き込む際に、デッドロックが発生する可能性があります。
次のコード例は、producerカーネルとconsumerカーネルで構成されています。チャネルc0とc1はバッファーされていないチャネルです。c0とc1からのチャネル読み出し動作のスケジュールは、c0とc1へのチャネル書き込み動作とは逆の順序で発生する可能性があります。つまり、producerカーネルはまずc0に書き込みますが、consumerカーネルはc1を最初に読み取る場合があります。このチャネル呼び出しのスケジューリングの変更は、consumerカーネルが空のチャネルから読み取っているため、デッドロックを引き起こす可能性があります。
__kernel void producer (__global const uint * src, const uint iterations) { for (int i = 0; i < iterations; i++) { write_channel_intel(c0, src[2*i]); write_channel_intel(c1, src[2*i+1]); } } __kernel void consumer (__global uint * dst, const uint iterations) { for (int i = 0; i < iterations; i++) { /*During compilation, the AOC might reorder the way the consumer kernel writes to memory to optimize memory access. Therefore, c1 might be read before c0, which is the reverse of what appears in code.*/ dst[2*i+1] = read_channel_intel(c0); dst[2*i] = read_channel_intel(c1); } }
channel uint c0 __attribute__((depth(0))); channel uint c1 __attribute__((depth(0))); __kernel void producer (__global const uint * src, const uint iterations) { for (int i = 0; i < iterations; i++) { write_channel_intel(c0, src[2*i]); mem_fence(CLK_CHANNEL_MEM_FENCE); write_channel_intel(c1, src[2*i+1]); } } __kernel void consumer (__global uint * dst; const uint iterations) { for (int i = 0; i < iterations; i++) { dst[2*i+1] = read_channel_intel(c0); mem_fence(CLK_CHANNEL_MEM_FENCE); dst[2*i] = read_channel_intel(c1); } }
この例でproducerカーネルのmem_fenceは、c0へのチャネル書き込み動作がc1よりも前に発生するようにしています。同様にconsumerカーネルのmem_fenceは、c0のチャネル読み取り動作がc1よりも前に発生するようにしています。
チャネル使用時のカーネル間におけるメモリーの一貫性の定義
__kernel void producer( __global const uint * src, const uint iterations ) { for(int i=0; i < iterations; i++) { write_channel_intel(c0, src[2*i]); mem_fence(CLK_CHANNEL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE); write_channel_intel(c1, src[2*i+1]); } }
このカーネルにおいてmem_fence関数は、c0への書き込み動作とsrc[2*i]へのメモリーアクセスが、c1への書き込み動作とsrc[2*i+1]へのメモリーアクセスよりも先に実行されるようにしています。これにより、データがc1に書き込まれる前に、c0に書き込まれたデータが読み取りチャネルから見えるようになります。