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

Can I use OpenACC to parallelize a big code which call some functions?

本文关键字:函数 代码 调用 可以使 OpenACC 并行化 我可以      更新时间:2023-10-16

我正在尝试将我的序列C代码并行化,并使用OpenACC(PGI编译器(将其卸载到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内核将整个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((分解成小部分吗?

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

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

这取决于调用树的深度。正如杰夫拉金所说,acc routine可以帮助你,但它只是走了这么远。通常,这些例程需要内联以创建一个大内核。GPU并不是真正为处理数千行代码的复杂内核而构建的——也就是说,即使它能工作,也很难让它具有性能。

在更复杂的情况下,方法是在I、j域中私有化调用图(我认为这是一些模拟的物理参数化(。也就是说,不是为一列或表面点计算所有内容,而是将更高维的数据传递给子例程,这样就可以并行I,j中的较小块。

附带说明:对于Fortran 90+,我已经为您构建了一个并行化工具,但恐怕它不支持C++。不过,也许它会启发你找到一个预处理解决方案。在我的情况下,我需要保持CPU性能,这可能会影响我上面提出的解决方案,但这可能不适用于您的情况。