Intel® FPGA SDK for OpenCL™ Pro Edition: 最佳实践实践指南

ID 683521
日期 9/26/2022
Public
文档目录

4.1.1. 通道和管道的表征

要在您的OpenCL™内核程序中实现通道或管道,请记住它们各自特定于 Intel® FPGA SDK for OpenCL™ 的表征。

默认行为

通道的默认行为是阻塞(blocking)。管道的默认行为是非阻塞(nonblocking)。

多个OpenCL内核的并发执行

您可并发执行多个OpenCL内核。要使能并发执行,请修改主机代码以实例化多个命令队列。每个并发执行内核都与一个单独的命令队列相关联。

重要:

特定于管道的考量:

Intel® FPGA SDK for OpenCL™ 编程指南确保与其它OpenCL SDK的兼容性中概述了OpenCL管道修改的内容。OpenCL管道修改允许您在SDK上运行您的内核。但不会最大化内核的吞吐量。OpenCL Specification版本2.0要求管道写入在先,然后才进行管道读取,这样该内核就不会从空的管道读取。因此,内核不能并发执行。然而因为 Intel® FPGA SDK for OpenCL™ 支持并发执行,所以可以修改您的主机应用程序和内核程序来利用该功能。修改后可以增加应用程序的吞吐量;但是您不能再将内核转接(port)到另外一个SDK。尽管存在此限制,但修改很少,并且也不需要太大的工作量来维护这两类代码。

要启用包含管道的内核的并发执行,请用blocking属性(即,__attribute__((blocking)))取代您内核代码中的depth属性。blocking属性会在read_pipewrite_pipe函数调用中引入阻塞(blocking)行为。该调用站点阻塞(block)内核执行,一直到管道的另一端准备就绪。

如果将blocking属性和depth属性都添加到您的内核中,当管道为空时,read_pipe仅调用一个块;而当管道填满时,write_pipe仅调用一个块。阻塞行为(Blocking behavior)会导致内核之间的隐式同步, 这样就迫使内核彼此之间以锁步(lock step)方式运行。

隐式内核同步

通过阻塞通道调用或阻塞管道调用隐式同步内核。请考虑如下示例:

表 14.  阻塞通道和管道调用实现内核同步
具有阻塞通道调用的内核 具有阻塞管道调用的内核
channel int c0; 

__kernel
void producer (__global int * in_buf) 
{ 
  for (int i = 0; i < 10; i++) 
  {   
    write_channel_intel (c0, in_buf[i]);
  } 
}

__kernel
void consumer (__global int * ret_buf) 
{                 
  for (int i = 0; i < 10; i++) 
  { 
    ret_buf[i] = read_channel_intel(c0);
  }
} 
__kernel
void producer (__global int * in_buf,
  write_only pipe int __attribute__
  ((blocking)) c0) 
{
  for (int i = 0; i < 10; i++) 
{
    write_pipe (c0, &in_buf[i]);
  }
}

__kernel
void consumer (__global int * ret_buf,
  read_only pipe int __attribute__
  ((blocking)) c0) 
{
  for (int i = 0; i < 10; i++) 
  {
    int x;
    read_pipe (c0, &x);
    ret_buf[i] = x;
  }
}

您可以同步内核,以便在每次循环迭代期间,producer内核写入数据,而consumer内核读取数据。如果producer中的write_channel_intel或者write_pipe调用没有写入任何数据,consumer阻塞并等待在read_channel_intelread_pipe调用,直到 producer发送有效数据,反之亦然。

调用之中的数据持久性

write_channel_intel调用将数据写入通道,或者write_pipe调用将数据写入管道后,数据将在工作组和NDRange调用中保持不变。work-item写入通道或管道的数据保留在该通道或管道中,直到另外一个work-item从中读取。此外,通道或管道中的数据顺序等同于对该通道或管道的写入操作的顺序,然而该顺序与执行写操作的work-item无关。

例如,如果多个work-item尝试同时访问通道或管道,则只有一个work-item可以访问。write_channel_intel调用或write_pipe调用将名为DATAX的特定work-item数据分别写入通道或管道。与此类似的是,第一个访问该通道或管道的work-item从中读取DATAX。这种顺序排列的写操作使得通道和管道成为内核之间共享数据的有效方式。

强制实施的Work-Item顺序

SDK强制实施work-item顺序来保持通道或管道的读写操作一致性。