【问题标题】:R: Advantages of using a Fortran subroutine with .Call and C/C++ wrapper instead of .Fortran?R:使用带有 .Call 和 C/C++ 包装器而不是 .Fortran 的 Fortran 子例程的优势?
【发布时间】:2013-02-16 09:52:56
【问题描述】:

我有一个 R 包,它使用大量 Fortran 子例程进行递归线性代数计算的嵌套循环(很大程度上取决于 BLAS 和 LAPACK 例程)。作为 Fortran 的接口,我使用 .Fortran 函数。我刚刚读到Jonathan Callahan's blog post 关于使用.Call 而不是.C 在用C/C++ 编写的子例程的情况下,这让我想到在使用Fortran 子例程时也使用.Call 接口会更好,通过编写C 中的一个简单包装器,然后调用 Fortran 子例程?

如前所述,我的 Fortran 代码在某种意义上非常简单,我只是使用双精度或整数类型的多维数组。但是我了解到我必须在 R 端写很多检查以确保一切都不会崩溃,因为我不小心忘记将某些矩阵的存储模式更改为整数或某些矩阵的维度已更改等。

子程序写成 F90/95。

【问题讨论】:

  • 似乎合理地将 .Call() 与某些 C 函数一起使用,然后您确实可以从 C 代码中调用您的 Fortran 子例程,这相对容易(如果您不这样做,甚至可以在 C 中执行所有操作真的需要 Fortran)。
  • 是的,但是如果有的话会带来什么样的好处呢?我可以完全切换到 C,但这太麻烦了,我怀疑它是否有用,因为无论如何我都会从 C 调用 Fortran BLAS 函数。
  • 你为什么要切换到这里?然后,您将拥有另一层抽象。调用 C 例程又调用 FORTRAN 永远不会比你拥有的更快,R -> FORTRAN。
  • 我认为没有那么简单,根据我的问题和其他来源的链接,我的理解是.C.Fortran 的开销比.Call 的开销要大得多至少因为参数的额外复制。与 R 等相比,C 中的类型检查可能存在一些性能差异,对此也不知道。

标签: c performance r fortran wrapper


【解决方案1】:

如果您使用的是大型数据集,可能会有优势。 .Call 可以更快,因为您每次调用函数时都不会复制数据。对于这个问题中描述的情况,不会有这样的优势,因为 R 2.15.1 版本说明状态

.C() 和 .Fortran() 复制较少:原始、逻辑、整数、实数或复数向量且未命名的参数在调用之前不复制,并且(命名或未命名)在调用之后不复制称呼。列表不再被复制(它们应该在 C 代码中以只读方式使用)。

切换到 .Call 意味着您放弃了 .Fortran 接口的便利性。您将 SEXP 传递到 C 代码中,使用(可怕且没有充分记录的)R API 对数据进行任何检查/操作,然后从 C 调用 Fortran 函数。任何使用您的代码的人都必须了解R API 和 C/Fortran 互操作。

【讨论】:

  • 是的,复制似乎是一个明显的好处,尽管从 R 2.15.1 开始,差异略有减少:“.C().Fortran() 减少复制:原始的、合乎逻辑的参数, “ (来自 R 新闻)。
  • 在我的应用程序中,复制的效果似乎并不显着;我使用DUP=FALSEDUP=TRUE 测试了使用.Fortran 调用我的代码,并且几乎没有发现任何差异,尽管我使用的是包含数百万个元素的数组。
  • 我不知道这些函数的行为发生了变化。我已经编辑了问题以反映它。
【解决方案2】:

R 包 dotCall64 可能是一个有趣的替代方案。它提供了.C64(),它是外来函数接口的增强版本,即.C().Fortran()

接口.C64() 可用于连接 Fortran 和 C/C++ 代码。它

  • .C().Fortran() 的用法相似
  • 提供一种机制来避免不必要的只读和只写参数副本
  • 支持长向量(超过 2^31-1 个元素的向量)
  • 支持 64 位整数类型参数

因此,可以避免不必要的只读参数副本,同时避免 .Call() 接口与 C 包装函数结合使用。

一些链接:

我是 dotCall64spam 的作者之一。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-14
    • 2012-01-02
    • 1970-01-01
    • 2017-02-26
    • 1970-01-01
    相关资源
    最近更新 更多