科学计算中的 F # 性能

我很好奇 F # 的性能和 C + + 的性能如何比较?关于 Java,我问了一个类似的问题,我得到的印象是 Java 不适合于繁重的数字处理。

我曾经读到过,F # 应该具有更高的可伸缩性和更高的性能,但是与 C + + 相比,现实世界中的性能如何呢?关于目前执行情况的具体问题是:

  • 它的浮点运算能力如何?
  • 它是否允许向量指令
  • 它对优化有多友好 编译器?
  • 它有多大的内存脚印? 它是否允许对内存位置进行细粒度控制?
  • 它是否有分布式的能力 存储器处理器,例如 Cray?
  • 在涉及大量数字处理的计算科学中,它有哪些特性可能会引起研究人员的兴趣?
  • 有没有真正的科学计算 使用它的实现?

谢谢

20765 次浏览

与所有语言/性能比较一样,您的收获很大程度上取决于您的编码能力。

F # 是 OCaml 的衍生物。我很惊讶地发现,OCaml 在金融领域被广泛使用,在这个领域,数据处理性能非常重要。更让我吃惊的是,我发现 OCaml 是速度最快的语言之一,其性能堪比最快的 C 和 C + + 编译器。

F # 是建立在 CLR之上的。在 CLR 中,代码以字节码的形式表示,称为通用中间语言。因此,如果代码编写得很好,它可以从 JIT 的优化功能中受益,并且具有与 C # 相当的性能(但不一定是 C + +)。

通过使用本机图像生成器(NGEN) ,CIL 代码可以在运行时之前的单独步骤中编译成本机代码。由于不再需要 CIL 到本机的编译,这将加速所有后续运行的软件。

需要考虑的一点是,像 F # 这样的函数式语言受益于更具声明性的编程风格。在某种意义上,您过度指定了命令式语言(如 C + +)中的解决方案,这限制了编译器进行优化的能力。从理论上讲,更具宣告式编程的风格可以为编译器提供更多算法优化的机会。

据我所知,大多数科学计算还是在 FORTRAN 完成的。对于线性代数问题,它仍然比其他任何问题都要快——不是 Java,不是 C,不是 C + + ,不是 C # ,不是 F # 。LINPACK 进行了很好的优化。

但是,“你的成绩可能会有所不同”这句话适用于所有的基准。笼统的陈述(除了我的)很少是真实的。

除了其他人所说的,F # 还有一个重点,那就是 并行性。普通 F # 代码的性能是由 CLR 决定的,尽管您可以从 F # 使用 LAPACK,或者您可以使用 C + +/CLI 作为项目的一部分进行本机调用。

然而,设计良好的函数式程序往往更容易并行化,这意味着您可以通过使用多核 CPU 轻松获得性能,如果您正在进行一些科学计算,那么您肯定可以使用多核 CPU。以下是一些相关链接:

关于分布式计算,你可以使用任何分布式计算框架。NET 平台。有一个 MPI.NET 项目,可以很好地使用 F # ,但是您也可以使用 DryadLINQ,这是一个 MSR 项目。

不幸的是,我不认为你会找到很多可靠的信息。F # 仍然是一种非常新的语言,因此即使它非常适合性能繁重的工作负载,也不会有太多有重要经验的人来报告。此外,性能很难准确衡量,微基准测试也很难推广。即使在 C + + 中,你也可以看到编译器之间的巨大差异——你是否想知道 F # 是否与 任何 C + + 编译器竞争,或者与假设的“最佳可能”C + + 可执行文件竞争?

至于针对 C + + 的具体基准测试,这里有一些可能相关的链接: O’caml vs f # : QR分解; 针对并行数值的 F # 与非托管 C + + 的比较。请注意,作为 F # 相关材料的作者和 F # 工具的供应商,作者对 F # 的成功有既得利益,因此对这些说法持保留态度。

我认为可以有把握地说,在一些应用程序中,F # 在执行时间上是有竞争力的,而在另一些应用程序中则可能没有竞争力。在大多数情况下,F # 可能需要更多的内存。当然,最终的性能也将高度依赖于程序员的技能-我认为 F # 几乎可以肯定是一种更有生产力的语言,适合中等水平的程序员使用。此外,我认为目前 Windows 上的 CLR 在大多数操作系统上比 Mono 在大多数任务上表现更好,这也可能影响你的决策。当然,由于 F # 可能比 C + + 更容易并行化,它还取决于您计划运行的硬件类型。

最后,我认为真正回答这个问题的唯一方法是编写代表您想要执行的计算类型的 F # 和 C + + 代码,并对它们进行比较。

这取决于你在做什么样的科学计算。

如果你正在做 traditional heavy computing,例如线性代数,各种各样的优化,那么你不应该把你的代码。Net 框架,至少不适合 F # 。因为这是在算法级别,所以大多数算法必须用命令式语言编写,以便在运行时间和内存使用方面有良好的性能。其他人提到了并行,我必须说,当您执行诸如并行 SVD 实现之类的低级工作时,它可能是无用的。因为当您知道如何并行 SVD 时,您就不会使用高级语言,Fortran、 C 或修改后的 C (例如 青菜)就是您的朋友。

然而,今天的许多科学计算都不是这种类型的,而是某种高层次的应用,例如统计计算和数据挖掘。在这些任务中,除了一些线性代数,或优化,还有很多数据流,IOs,抢占,做图形等。对于这些任务,F # 非常强大,因为它简洁、实用、安全、易于并行等等。

正如其他人提到的。Net 很好地支持平台调用,实际上 MS 内部有相当多的项目正在使用。网络和 P/调用一起提高瓶颈处的性能。

下面是我可以分享的两个例子:

  1. 矩阵乘法 我有一篇博客文章 比较不同的矩阵乘法实施方案

  2. LBFGS

我有一个使用 lbfGS 优化的大规模 Logit模型求解器,它是用 C + + 编写的。实现经过了良好的调优。我在 C + +/CLI 中修改了一些代码,也就是说,我将代码编译成。网。那个。Net 版本比在不同数据集上编译的初始版本慢3到5倍。如果您使用 F # 编写 LBFGS,那么性能不会比 C + +/CLI 或 C # 更好(但会非常接近)。

我还有一篇关于 为什么 F # 是数据挖掘的语言的文章,虽然与你在这里关心的性能问题没有太大关系,但是它与 F # 中的科学计算有很大关系。

  • F # 执行浮点计算的速度与。NET CLR 将允许它。与 C # 或其他语言没有太大区别。NET 语言。
  • F # 本身不支持向量指令,但是如果 CLR 为这些向量指令提供了 API,那么 F # 使用它应该不会有问题。例如,请参见 单核细胞增多症
  • 据我所知,目前只有一个 F # 编译器,所以也许问题应该是“ F # 编译器在优化方面有多好?”.无论如何,答案都是“可能与 C # 编译器一样好,但目前可能更差一点”。注意,F # 不同于 C # ,它支持在编译时内联,这可能允许依赖于泛型的更高效的代码。
  • F # 程序的内存脚印与其他程序的内存脚印相似。NET 语言。您对分配和垃圾收集的控制量与其他。NET 语言。
  • 我不知道是否支持分布式内存。
  • F # 有很好的原语来处理平面数据结构,例如数组和列表。例如,看看 Array 模块的内容: map、 map2、 mapi、 iter、 fold、 zip... ... 数组在科学计算中非常流行,我猜是因为它们内在的良好内存局部性。
  • 对于使用 F # 的计算科学包,你可能想看看 Jon Harrop 在做什么。

我很好奇 F # 的性能和 C + + 的性能如何比较?

根据应用程序的不同,变化很大。如果您在多线程程序中广泛使用复杂的数据结构,那么 F # 可能是一个很大的胜利。如果你的大部分时间都花在紧凑的数字循环变异数组上,那么 C + + 可能会快2-3倍。

案例研究: 光线跟踪器 我的基准 给你使用一个树来进行分层筛选,并使用数字光线-球面相交代码来生成输出图像。这个基准测试已经有好几年的历史了,C + + 代码在过去的几十年中已经被改进了几十次,并且被成千上万的人阅读。微软的 Don Syme 设法编写了一个 F # 实现,当使用 MSVC 编译并使用 OpenMP 并行化时,它比最快的 C + + 代码略快一些。

我曾经读到过,F # 应该具有更高的可伸缩性和更高的性能,但是与 C + + 相比,现实世界中的性能如何呢?

使用 F # 开发代码比使用 C + + 更容易、更快,这适用于优化和维护。因此,当您开始优化一个程序时,如果使用 F # 而不是 C + + ,相同的工作量将产生更大的性能收益。然而,F # 是一种高级语言,因此性能上限较低。因此,如果你有无限的时间来进行优化,理论上,你应该总是能够在 C + + 中生成更快的代码。

当然,这与 C + + 相对于 Fortran 和 Fortran 相对于手写汇编程序的好处是完全一样的。

案例分析: QR分解 这是一个基本的数值方法,来自于 LAPACK 等库提供的线性代数。参考 LAPACK 实现是 Fortran 的2,077行。我用不到80行的代码编写了 F # 实现,这些代码达到了相同的性能水平。但是参考实现并不快: 像 Intel 的 Math Kernel Library (MKL)这样经过厂商调优的实现通常要快10倍。值得注意的是,我成功地优化了我的 F # 代码,超越在 Intel 硬件上运行的 Intel 实现的性能,同时保持我的代码在150行以下,并且完全通用(它可以处理单精度和双精度,以及复杂甚至符号矩阵: 对于高瘦矩阵,我的 F # 代码比 Intel MKL 快3倍。

请注意,这个案例研究的寓意并不是你应该期望你的 F # 比供应商调优的库更快,而是,即使像 Intel 这样的专家如果只使用低级语言,也会错过高效的高级优化。我怀疑英特尔的数值优化专家没有充分利用并行性,因为他们的工具使它非常笨重,而 F # 使它不费吹灰之力。

它的浮点运算能力如何?

性能类似于 ANSI C,但是一些功能(例如舍入模式)不能从.NET 获得。

它是否允许向量指令

没有。

它对优化编译器有多友好?

这个问题没有意义: F # 是微软的一种专有.NET 语言,只有一个编译器。

它的记忆足迹有多大?

一个空应用程序在这里使用1.3 Mb。

它是否允许对内存区域进行细粒度控制?

比大多数内存安全语言都要好,但是不如 C。例如,你可以用“ structs”来表示 F # 中的任意数据结构。

它是否具有分布式内存处理器的容量,例如 Cray?

这取决于你所说的“容量”是什么意思。如果你能跑的话。NET,然后你可以使用消息传递在 F # (就像下一种语言) ,但 F # 主要是为桌面多核 x86机器。

在涉及大量数字处理的计算科学中,它有哪些特性可能会引起研究人员的兴趣?

内存安全意味着不会出现分段错误和访问冲突。中对并行性的支持。NET 4很好。在 Visual Studio 2010中,通过 F # 交互式会话动态执行代码的能力对于交互式技术计算非常有用。

是否有实际的科学计算实现使用它?

我们用于 F # 科学计算的 商业产品已经有数百个用户。

然而,你提出的问题表明,你认为科学计算是高性能计算(如 Cray) ,而不是交互式技术计算(如 MATLAB、 Mathematica)。F # 是为后者设计的。

首先,C 语言明显比 C + + 快。.所以如果你需要这么大的速度,你应该把 lib 等变成 c。

关于 F # ,大多数基准使用 Mono,它比 MS CLR 慢2 * ,部分原因是它使用了 boehm GC (他们有一个新的 GC 和 LVVM,但这些还不成熟,不支持泛型等)。

.NET 语言本身被编译成一个 IR (CIL) ,它可以像 C + + 一样高效地编译成本地代码。大多数 GC 语言都会遇到一个问题集,那就是大量的可变写操作(包括 C + +)。NET).有一个特定的科学问题集需要这样做,这些问题在需要的时候可能应该使用本地库或者使用享元模式来重用池中的对象(这样可以减少写操作)。原因是在。NET CLR,当更新引用字段(包括框)时,它将在表中设置一个位,表示该表已修改。如果您的代码包含大量这样的写操作,那么它将受到影响。

就是说。像 C # 这样的 NET 应用程序使用大量的静态代码、结构和结构上的 ref/out 可以产生类似 C 的性能,但是像这样编写代码或者维护代码(像 C)是非常困难的。

然而,F # 的亮点在于不可变数据的并行性,这会带来更多基于读的问题。值得注意的是,大多数基准测试在可变写操作中比实际应用程序中要高得多。

关于浮点数,您应该使用另一个 lib (即。由于速度缓慢,网络一)到 oCaml 的。C/C + + 允许更快的速度以达到更低的精度,而 oCaml 在默认情况下并不这样做。

最后,我认为像 C # 、 F # 这样的高级语言和正确的分析在同样的开发时间内会比 c 和 C + + 给你更好的效果。如果您将瓶颈更改为 c lib paccess 调用,那么在关键领域也将得到与 C 类似的性能。也就是说,如果你有无限的预算,并且更关心速度,那么维护比 C 更好(而不是 C + +)。

如果我说“2-3年后再问一次”,我想这会完全回答你的问题: -)

首先,不要期望 F # 与 C # 有什么不同,除非你是故意做一些复杂的递归,我猜你没有,因为你问了数值。

从浮点数的角度来看,它肯定比 Java 要好,因为 CLR 并不致力于实现跨平台的一致性,这意味着 JIT 只要有可能就会升级到80位。另一方面,除了观察变量的数量以确保有足够的 FP 寄存器之外,你不能控制它。

向量方面,如果你叫得够大声,也许在2-3年内会发生一些事情,因为 Direct3D 正在进入。NET 作为一个通用的 API,而 XNA 中的 C # 代码运行在 Xbox 上,这与使用 CLR 所能得到的最接近的东西。这仍然意味着您需要自己编写一些中间代码。

因此,不要期望 CUDA 或甚至能够只是链接 NVIDIA 库和开始。如果出于某种原因,您真的非常非常需要一种“函数式”语言,那么您在使用 Haskell 时会有更多的运气,因为 Haskell 被设计为纯粹出于需要的链接友好型语言。

单核细胞增多症。Simd 已经被提到过了,虽然它应该可以移植到 CLR 中,但是要真正做到这一点可能需要做很多工作。

中使用 SSE3的 社交网站中有相当多的代码。NET,通过 C + +/CLI 和 C # ,来进行数组编码,为 perf 注入 SSE3代码等。

有一些关于在已编译的 C # 上运行 CECIL将部分提取到 HLSL 中,编译成着色器并链接一个胶合代码来调度它(CUDA 正在做类似的工作)的讨论,但是我不认为有任何可以运行的东西。

如果你想尽快尝试一些东西,在 codelx 上的 phyX.Net对你来说可能更有价值。不要指望它只是打开行李,然后施展魔法。然而,ih 目前有一个活跃的 author,代码都是普通的 C + + 和 C + +/CLI,如果你想了解更多细节,或许可以使用类似的方法进行 CUDA,你可以从作者那里得到一些帮助。对于全速 CUDA,你仍然需要编译你自己的内核,然后只是接口到。NET,所以越容易的一部分去,你会更快乐。

有一个应该是免费的 CUDA.NET库,但是页面只提供了电子邮件地址,所以期待一些字符串附加,而作者写了一个 博客,他并不特别谈论库里面有什么。

哦,如果你有预算,你可以给那个 Psi Lambda一个看看(KappaCUDAnet 是。NET 部分)。显然,他们打算在11月提高价格(如果这不是一个销售伎俩的话: -)