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

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

我正在尝试并行化我的顺序 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 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() 分成小部分?

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

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

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

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

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