我可以使用 OpenACC 并行化调用某些函数的大代码吗?

Posted

技术标签:

【中文标题】我可以使用 OpenACC 并行化调用某些函数的大代码吗?【英文标题】:Can I use OpenACC to parallelize a big code which call some functions? 【发布时间】:2015-10-30 11:21:00 【问题描述】:

我正在尝试使用 OpenACC(PGI 编译器)并行化我的顺序 C 代码并卸载到 NVIDIA GPU

我的代码是按顺序编写的。并且经常调用很长的函数,如下所示。

int main()

   // blah blah...
   for(i=0; i<10; i++)
   
      for(j=0; j<20; j++)
      
          big_function(a,b,c);
      
   
   // blah blah...


int big_function(a,b,c)

   small_function_1(a);
   small_function_2_with_data_dependencies(b);

那种情况,big_function()可以并行运行在GPU上吗?

我使用 #pragma acc kernels 将整个 for 循环声明为并行区域。如下所示。

#pragma acc routine
int big_function(int a, int b, int c);
#pragma acc routine
int small_function_1(int a);
#pragma acc routine
int small_function_2_with_data_dependencies(int b);

int main()

   // blah blah...
   #pragma acc data ~~~~
   #pragma acc kernels
   for(i=0; i<10; i++)
   
      for(j=0; j<20; j++)
      
          big_function(a,b,c);
      
   
   // blah blah...


int big_function(a,b,c)

   small_function_1(a);
   small_function_2_with_data_dependencies(b);

但是编译后的文件需要很长时间才能完成。结果不正确。

我可以使用 OpenACC 来并行化使用许多函数调用的顺序代码吗?

或者我是否必须将 big_function() 分解为小部分?

【问题讨论】:

【参考方案1】:

您需要使用 acc routine 指令在调用树中装饰每个函数,就像您在示例中所做的那样。如果您希望所有并行性都来自顶层循环,那么您将希望将所有例程标记为顺序 (seq)。只要您这样做了,编译器应该就能够为 GPU 构建它。不过,您很可能会获得较差的性能,因为像这样的大型函数调用树往往包含大量状态,这会消耗 GPU 资源、共享内存和寄存器。您可能会发现,如果将并行性移到调用树下,它在 GPU 上的性能会好得多,但这可能会对 CPU 性能产生负面影响,并且可能会增加内存使用量,因为您必须保存以前的数据可作为线程状态使用。

如果您可以提供有关实际代码的更多信息,我可以尝试帮助您调试正确性问题。您应该检查编译器反馈 (-Minfo) 并确保编译器正在执行您认为正在执行的操作。您可能会发现它被调用树绊倒了。您也可以尝试 PGI 论坛,因为它们通常对那里的帮助查询非常敏感。

【讨论】:

感谢您的回复。很抱歉,我无法提供我的实际代码,因为这是我公司的项目。 我还有一个问题。为什么长函数会出现性能不佳?相同的代码在 CPU 上运行得很好,而且性能很好。通过 CPU 和 GPU 运行代码有什么不同的东西吗? 在 CPU 上,一次运行的线程数量相对较少,因此维护每个线程的状态不需要大量资源。这是因为切换线程很昂贵。在 GPU 上,您在任何给定时间都有数量级的线程,因此必须维护的状态量也会增加,导致在 GPU 上使用大量寄存器、共享内存和本地内存。随着这些数量的增加,GPU 上一次可以驻留的线程越来越少,这就是所谓的低占用率,因此 GPU 将没有那么多的并行性来提高性能。【参考方案2】:

这取决于你的调用树的深度。正如 jefflarkin 所说,acc routine 可以帮助您,但仅此而已。一般来说,这些例程需要内联才能创建一个大内核。 GPU 并不是真正为处理具有数千行代码的复杂内核而构建的——也就是说,即使它可以工作,也很难让它发挥作用。

在更复杂的情况下执行此操作的方法是在 i,j 域中私有化您的调用图(我假设这是某些模拟的物理参数化)。 IE。您无需为一列或表面点计算所有内容,而是将更高维度的数据传递给您的子例程,因此您可以并行化 i,j 中的较小块。

旁注:对于 Fortran 90+,我构建了一个 tool 来为您执行并行化,但恐怕它不支持 C++。也许它会激发您的预处理解决方案。就我而言,我需要保持 CPU 性能,这可能会受到我上面提出的解决方案的影响,但这可能不适用于您的情况。

【讨论】:

感谢您的回复。我会尝试私有化我的函数的调用树。

以上是关于我可以使用 OpenACC 并行化调用某些函数的大代码吗?的主要内容,如果未能解决你的问题,请参考以下文章

OpenACC + MPI Fortran 程序入门

谁能解释如何在 GCC 中使用 OpenACC?

OpenACC 计算构建内的自定义函数

开放式加速器 | Fortran 90:并行化嵌套 DO 循环的最佳方法是啥?

Openacc:如何使插入排序更加并行[关闭]

使用 data.table 并行化/多线程