Fortran 调用 C:如何获得有效的矢量化函数

Posted

技术标签:

【中文标题】Fortran 调用 C:如何获得有效的矢量化函数【英文标题】:Fortran calling C: How do I get an efficient vectorised function 【发布时间】:2016-07-04 13:00:12 【问题描述】:

我必须从 Fortran 调用 C 函数,但我想在向量化循环中执行此操作。我正在 Linux 上使用 Intel 16.0.3 编译器。

所以选项是:我可以尝试将函数内联,或者我可以使用 SIMD 函数(我想为此使用 OpenMP SIMD,我希望它是可移植的,并且我已经在使用 OpenMP)。

如果我从 Fortran 调用 Fortran,它可以双向工作。对于传递参数,我使用 linear/ref 子句来传递对值向量而不是引用向量的引用,这似乎有效。但在 C 中,linear/ref 子句不被识别。

我可以让函数名义上矢量化,但它正在插入聚集和分散,性能并不比标量好(至少对于我的小测试函数而言)。

如果我将 linear(ref(r,s)) 放在 Fortran 接口块中,我会收到消息

UVAL 或 REF 修饰符不得用于带有 VALUE 属性。

我可以使用从 Fortran 传递值并返回一个值作为函数返回的技巧来获得性能。这会产生一个向量化的函数,并且性能很好,但不幸的是我的真实函数需要返回多个值。

如果我尝试内联 C 函数,它将无法正常工作。 opt-report 只是告诉我调用站点不能内联。即使使用标量函数也是如此。我根本无法让 C 函数内联到 Fortran。我正在使用 ipo 来尝试这样做。我想知道内联问题是否可能是 Fortran 将指针传递给指针?但是随着代码给出了正确的答案,它似乎在某种程度上可以接受。

测试代码(传递指针)本质上是……

Fortran 调用者

Use, intrinsic :: ISO_C_BINDING
….
real*8, allocatable :: r(:),s(:)  .
….

interface
 integer simd_c_func(r,s) bind(c, name="simd_c_func")
 !$OMP DECLARE SIMD(simd_c_func)
 import :: C_DOUBLE
 real(kind=C_DOUBLE), intent(inout):: r,s
end interface

allocate.... 

!$OMP SIMD

do i=1,N
  ierror=simd_c_func(r(i),s(i))
enddo

C被调用者

#pragma omp declare simd
int simd_c_func(double *r, double *s) 
 (*r)+=(*s);
 return 0;

【问题讨论】:

你能做一个可以编译的测试程序吗? 这些东西很新,支持不完整,而且因编译器版本而异。包含实际的编译器输出并尝试制作一个 MCVE ***.com/help/mcve 好的,我会尽快粘贴一个工作示例。我在带有相关编译器包的独立机器上拥有代码。正在等待转移。 【参考方案1】:

LINEAR(REF()) 很新,所以只能希望一些使用 OMP 和 CONTIGUOUS 的 LINEAR 可以让你开心。

消息:

UVAL 或 REF 修饰符不得用于具有 VALUE 属性的虚拟参数。"

表示它正在做值并在堆栈上。我以为 C 数组在 heap 中?但也许你需要一个-heap 开关?

C 端:

我建议你加linear给你#pragma

#pragma omp declare simd linear(r,s)

您是否考虑过使用#pragma vector aligned?还有#pragma ivdep? 我假设 (*r) 是一个引用(在堆中)

F90 侧

我建议你更换:

real(8), allocatable :: r(:), s(:)

!$DIR ATTRIBUTES ALIGN:64                           :: R, S
REAL(KIND=8), DIMENSION(:), ALLOCATABLE, CONTIGUOUS :: R, S

这里唯一真正的变化是 64 位对齐和连续。 也可以使用 switch -align array64byte

如果ierror 不是一个数组,那么你想要一个吗?或者您是否可能需要在ierror 上使用第一个/最后一个私有或缩减子句?我可能会使用归约。

!$OMP SIMD 然后转到:

!$OMP DO SIMD REDUCTION(ierror)

但正如您提到的,您需要多个值,那么您可能需要分配ierror

我对接口如何知道它的返回值为零感兴趣。好像你必须在界面的某个地方有 INTEGER FUNCTION。

链接编译端:

如果 C 生成了 F90- MODULE,则 USE 将允许它深入了解 C 代码以内联或以其他方式提供帮助。所以你可能需要编译器/链接上的-IPO 来让 F90 了解它可以对 C 调用者做什么。

【讨论】:

【参考方案2】:

睡一觉后,我想知道你是否应该只是有一个 void,并在你的接口规范上使用子程序?

另一个选项似乎是: INTEGER FUNCTION simd_c_func 然后在 FORTRAN 端的 SIMD 中使用 REDUCTION。

但是这些都没有解决您收到的实际编译器消息。 我看到你已经使用了-IPO。

您可以尝试替换: 线性(REF(R,S)) 和 线性(REF(R), REF(S))

【讨论】:

这可能是对您之前答案的修改。水平标尺 (---) 可以很好地分隔各个部分,并且可以使用大字体的标题。所以我建议进行编辑,然后删除第二个答案。欢迎来到 ***,顺便说一句。我不知道编译器可以在语言之间进行链接时间优化。这很酷。 (不过我不知道 fortran;因为 SIMD 标签,我看到了这个问题,所以我自己不能在这个问题上添加任何东西。)。 另外,如果this is you as well,您应该将您的帐户合并为一个 Peter 如何合并它们?我似乎找不到它。 IDK,我从来没有创建过第二个帐户。我认为这应该是可能的;尝试搜索 meta.***.com 或 meta.stackexchange.com

以上是关于Fortran 调用 C:如何获得有效的矢量化函数的主要内容,如果未能解决你的问题,请参考以下文章

C语言调用Fortran

怎样从 C 中调用 FORTRAN (C++, BASIC, Pascal, Ada, LISP) 的函数?反之亦然?

使用 Fortran 中的内存数据调用 C 代码

从 C++ 调用带有可选参数的 Fortran 子例程

从 C++ 调用带有可选参数的 Fortran 子例程

在 Fortran 派生类型中保存指向 C 函数的指针