73

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

我已经读过 F# 应该更具可扩展性和更高的性能,但是与 C++ 相比,这种实际性能如何?关于当前实施的具体问题是:

  • 它在浮点方面做得如何?
  • 是否允许向量指令
  • 它对优化编译器有多友好?
  • 它有多大的内存足迹?它是否允许对内存位置进行细粒度控制?
  • 它是否具有分布式内存处理器的容量,例如 Cray?
  • 它有哪些特征可能对涉及大量数字处理的计算科学感兴趣?
  • 是否有使用它的实际科学计算实现?

谢谢

4

10 回答 10

65

我很好奇 F# 性能与 C++ 性能相比如何?

因应用程序而异。如果您在多线程程序中广泛使用复杂的数据结构,那么 F# 可能会是一个巨大的胜利。如果你的大部分时间都花在紧密的数值循环上,那么 C++ 可能会快 2-3 倍。

案例研究:光线追踪器我的基准测试使用树进行分层剔除和数值光线球相交代码来生成输出图像。这个基准已经有几年的历史了,C++ 代码多年来已经改进了数十次,并被数十万人阅读。Microsoft 的 Don Syme 设法编写了一个 F# 实现,当使用 MSVC 编译并使用 OpenMP 并行化时,它比最快的 C++ 代码略快。

我读过 F# 应该更具可扩展性和更高的性能,但是与 C++ 相比,这种实际性能如何?

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

当然,这与 C++ 优于 Fortran 和 Fortran 优于手写汇编程序的优势完全相同。

案例研究:QR 分解这是 LAPACK 等库提供的线性代数的基本数值方法。参考 LAPACK 实现是 2,077 行 Fortran。我用不到 80 行代码编写了一个 F# 实现,实现了相同的性能水平。但参考实现并不快:英特尔的数学内核库 (MKL) 等供应商调整的实现通常快 10 倍。值得注意的是,我设法优化了我的 F# 代码,远远超出了在英特尔硬件上运行的英特尔实现的性能,同时将我的代码保持在 150 行代码以下并且完全通用(它可以处理单精度和双精度,以及复杂甚至符号矩阵!):对于高薄矩阵,我的 F# 代码比英特尔 MKL 快 3 倍。

请注意,本案例研究的寓意并不是您应该期望您的 F# 比供应商调整的库更快,而是如果他们只使用较低级别的语言,即使是英特尔这样的专家也会错过高效的高级优化。我怀疑英特尔的数值优化专家未能充分利用并行性,因为他们的工具使其极其繁琐,而 F# 使其毫不费力。

它在浮点方面做得如何?

性能类似于 ANSI C,但某些功能(例如舍入模式)在 .NET 中不可用。

是否允许向量指令

不。

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

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

它有多大的内存足迹?

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

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

比大多数内存安全语言更好,但不如 C。例如,您可以通过将 F# 中的任意数据结构表示为“结构”来拆箱。

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

取决于您所说的“容量”。如果您可以在该 Cray 上运行 .NET,那么您可以使用 F# 中的消息传递(就像下一种语言一样),但 F# 主要用于桌面多核 x86 机器。

它有哪些特征可能对涉及大量数字处理的计算科学感兴趣?

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

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

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

但是,您的提问表明您将科学计算视为高性能计算(例如 Cray)而不是交互式技术计算(例如 MATLAB、Mathematica)。F# 适用于后者。

于 2010-05-10T00:45:51.137 回答
44

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

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

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

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

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

F# 是 OCaml 的衍生物。我惊讶地发现 OCaml 在金融世界中被大量使用,其中数字运算性能非常重要。我更惊讶地发现 OCaml 是速度更快的语言之一,其性能与最快的 C 和 C++ 编译器相当。

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

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

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

于 2010-05-02T02:17:51.790 回答
9

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

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

然而,今天的很多科学计算都不是这种类型的,而是某种高级应用,例如统计计算和数据挖掘。在这些任务中,除了一些线性代数,或者优化之外,还有大量的数据流、IO、预处理、做图形等。对于这些任务,F#真的很强大,因为它简洁、实用、安全、易于操作平行等

正如其他人所说,.Net很好地支持Platform Invoke,实际上MS内部相当多的项目是使用.Net和P/Invoke一起来提高瓶颈处的性能。

于 2010-05-03T01:50:57.680 回答
7

不幸的是,我认为您不会找到很多可靠的信息。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++ 代码并比较它们。

于 2010-05-02T17:43:56.627 回答
4

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

  1. 矩阵乘法:我有一篇博文比较不同的矩阵乘法实现

  2. LBFGS

我有一个使用 LBFGS 优化的大规模逻辑回归求解器,它是用 C++ 编码的。实现是很好的调整。我将一些代码修改为 C++/CLI 中的代码,即将代码编译成 .Net。.Net 版本比在不同数据集上简单编译的版本慢 3 到 5 倍。如果你在 F# 中编写 LBFGS,性能不会比 C++/CLI 或 C# 好,(但会非常接近)。

我还有一篇关于为什么 F# 是数据挖掘语言的帖子,虽然与您在这里关注的性能问题不太相关,但它与 F# 中的科学计算非常相关。

于 2010-05-05T01:29:28.453 回答
3

如果我说“2-3 年后再问”,我认为这将完全回答您的问题 :-)

首先,不要期望 F# 在性能方面与 C# 有任何不同,除非您故意进行一些复杂的递归,而且我猜您不是,因为您询问了数字。

在浮点方面它肯定会比 Java 更好,因为 CLR 不针对跨平台统一性,这意味着 JIT 将尽可能使用 80 位。另一方面,除了观察变量的数量以确保有足够的 FP 寄存器之外,您无法控制它。

就矢量而言,如果你尖叫得足够大声,可能会在 2-3 年内发生一些事情,因为 Direct3D 无论如何都作为通用 API 进入 .NET 并且在 XNA 中完成的 C# 代码在 Xbox 上运行,与 CLR 可以获得的裸机一样接近. 这仍然意味着您需要自己执行一些中间代码。

因此,不要指望 CUDA 甚至能够链接 NVIDIA 库并开始工作。如果出于某种原因您真的非常需要一种“功能性”语言,那么您将有更多的运气尝试使用 Haskell 的这种方法,因为 Haskell 被设计为出于纯粹的需要而对链接友好。

Mono.Simd 已经提到过,虽然它应该可以向后移植到 CLR,但实际上要完成它可能需要相当多的工作。

在social.msdn 发布的关于在 .NET、vith C++/CLI 和 C# 中使用 SSE3 的帖子中有相当多的代码,包括数组 blitting、为性能注入 SSE3 代码等。

有一些关于在编译后的 C# 上运行CECIL以将部分提取到 HLSL、编译到着色器并链接胶水代码来安排它的讨论(CUDA 无论如何都在做等效的事情),但我认为没有任何可运行的东西出来。

如果您想尽快尝试某些东西,可能对您更有价值的是Codeplex 上的 PhysX.Net。不要指望它只是打开包装并施展魔法。但是,ih 目前有活跃的作者,代码都是普通的 C++ 和 C++/CLI,如果你想深入了解细节并可能对 CUDA 使用类似的方法,yopu 可能会从作者那里得到一些帮助。对于全速 CUDA,您仍然需要编译自己的内核,然后只需与 .NET 接口,这样该部分越容易,您就会越快乐。

有一个CUDA.NET库,它应该是免费的,但该页面只提供了电子邮件地址,所以期待附加一些字符串,虽然作者写了一篇博客,但他并没有特别谈论库中的内容。

哦,如果你有预算,你可以看看Psi Lambda(KappaCUDAnet 是 .NET 部分)。显然,他们将在 11 月抬高价格(如果这不是销售技巧的话 :-)

于 2010-10-13T12:30:27.563 回答
2

首先,C 比 C++ 快得多。所以如果你需要这么快的速度,你应该在 c 中制作 lib 等。

关于 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# 的亮点在于不可变数据的并行性,这与更多基于读取的问题密切相关。值得注意的是,可变写入中的大多数基准比现实生活中的应用程序要高得多。

关于浮点,您应该使用 oCaml 的替代库(即 .Net 库),因为它很慢。C/C++ 允许更快地获得较低的精度,而 oCaml 默认情况下是不允许的。

最后,我认为像 C#、F# 这样的高级语言和适当的分析将在相同的开发时间为您提供比 c 和 C++ 更好的性能。如果您将瓶颈更改为 ac lib pinvoke 调用,您最终也会在关键区域获得类似 C 的性能。也就是说,如果您有无限的预算并且更关心速度,那么维护比 C 是要走的路(不是 C++)。

于 2010-06-18T00:59:55.587 回答
1

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

但是关于“你的里程可能会有所不同”的评论适用于所有基准测试。一揽子陈述(我的除外)很少是真的。

于 2010-05-02T02:21:43.603 回答