科学计算中的F#性能


72

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

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

  • 浮点运算的效果如何?
  • 它允许矢量指令吗
  • 对优化编译器有多友好?
  • 它有多少内存占用空间?是否允许对内存局部性进行细粒度控制?
  • 它是否具有用于分布式存储处理器(例如Cray)的容量?
  • 在涉及大量处理的计算科学中,它具有哪些有趣的功能?
  • 是否有使用它的实际科学计算实现?

谢谢


我从标题中删除了C ++,使其变得非对抗性的。但是我想知道相对于C ++的性能(所以我可以
谈一下

Answers:


40
  • 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在做什么。

9
我只想指出问题是F#vs C ++,而答案是F#vs C#,而C ++和C#是不同的语言。
Matthieu M.

64

我很好奇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实现是Fortran的2,077行。我用不到80行代码编写了一个F#实现,可以实现相同的性能水平。但是参考实现并不很快:像英特尔的数学内核库(MKL)这样的厂商调整的实现通常快10倍。值得注意的是,我设法很好地优化了F#代码超越使其性能了在英特尔硬件上运行的英特尔实施性能,同时将我的代码保持在150行以下的代码中,并且代码完全通用(它可以处理单精度和双精度,复杂甚至符号矩阵!):对于较高的薄矩阵,我的F#代码比Intel MKL快3倍。

请注意,此案例研究的目的并不是要让您的F#比供应商调整的库更快,而是,即使是像Intel这样的专家,如果他们仅使用低级语言也将错过有效的高级优化。我怀疑英特尔的数值优化专家未能充分利用并行性,因为他们的工具非常繁琐,而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#用于后者。


在我之前的评论中,我正在考虑您所说的高性能计算,而不是交互式。
duffymo,2010年

2
您还没有发布过据称胜过MATLAB的F#功能:
ZXX

1
@Jon Harrop的记忆位置?比大多数内存安全语言要好,但不如C语言好。对于C,存在用于此类位置控制的哪些选项,而这些选项在F#中不可用?这是语言还是平台限制?谢谢
user492238

@ user492238:在C语言中,您可以执行类似走私指针中的位的操作,并获取指向堆分配的内存块中间的内部指针。垃圾收集的语言几乎总是禁止这样做。因此,有一些牺牲,但它们相对较小。
JD 2012年

2
这篇文章充满了没有根据的断言。F#可以让您创建比C ++更高性能的代码的想法尤其令人质疑。我已经非常深入地参与了F#,包括许多PR来加速高阶Array函数,我可以向您保证,通常情况并非如此。F#的创建者可以用F#创建比C ++更快的东西,这可能比每种语言的先天特性对您在每种语言中的相对才能说的更多。
jackmott

44

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

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

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


16

与所有语言/性能比较一样,您的工作量很大程度上取决于您的编码水平。

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

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

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

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


有趣。我的世界在某种程度上仅限于fortran和C ++,但随后尝试扩大我的视野。我尚未真正在自己的领域中看到过OCaml应用程序
Anycorn

@Robert Harvey-我也听说过OCaml。快速的性能和小的代码。
Onorio Catenacci 2010年

但是,F#是在.NET中实现的,这意味着F#继承了有关规范过度的一些问题。F#函数是幕后的.NET方法,由于它们可能会产生副作用,因此可以保证按特定的顺序执行这些方法-即使F#在99%的时间中没有这些或您不在乎它们的顺序(例如调试/记录语句)。因此,我警告不要期望F#带来太多性能-很好;它可能很快就可以了-但是它主要是出于功能性而不是可优化性而简洁。
伊蒙·纳邦

2
是的,因此,如果您使用内联函数并且使用无副作用的操作(即没有.NET互操作),那么它可以重新排序。不幸的是,正如可以使用反射器验证的那样,普通的F#函数编译为.NET方法。MS本身在有关内联函数的MSDN页面上说:“除非尝试了所有其他优化技术,否则应避免使用内联函数进行优化”。但是,即使您这样做,F#也会进行哪些优化来使C ++(静态内联)无法实现类似的代码?在手动帮助下,我确信F#是朝正确方向迈出的一步-但这不是Haskell。
伊蒙·纳邦

1
我要说的不是在特定情况下F#不可能拥有特定优势,而是不应该使人们相信这些优势以任何方式都是自动的,甚至总是可以实现的。从语义上讲,该语言与C#并没有什么不同-即使它鼓励您在本地范围内使用无副作用的结构,即使当前编译器使用的信息比C#当前的编译器更好。我真的看不到F#的语义如何在C ++之上实现更多新的编译器优化。没有魔术子弹,这……
Eamon Nerbonne 2010年

9

这取决于您正在执行哪种科学计算。

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

但是,当今许多科学计算都不是这种类型,它是某种高级应用程序,例如统计计算和数据挖掘。在这些任务中,除了一些线性代数或优化之外,还存在大量数据流,IO,预设,制作图形等。对于这些任务,F#确实功能强大,因为其简洁,功能,安全,易于操作。并行等

正如其他人提到的那样,.Net很好地支持Platform Invoke,实际上MS中的许多项目都一起使用.Net和P / Invoke来改善瓶颈性能。


“在算法级别,大多数算法必须使用命令式语言进行编码,以在运行时间和内存使用方面具有良好的性能。” [需要引用]
朱丽叶

2
这些算法的运行时间是用触发器衡量的,高级语言很难衡量。内存使用情况也很难预测,在C和Fortran中,您可以精确计算要使用多少字节。
尹铸2010年

2
“通过命令式语言更容易确定性能”与“仅命令式语言才能提供​​良好的性能”非常不同。而且也是错误的。诸如高速缓存一致性之类的二阶效应在现代处理器上是如此重要,以至于无法测量FLOP中的算法。在需要优化FLOP的算法和需要10倍FLOP的局部性优化算法之间,局部性优化算法将获胜。在我之后重复:FPU不再是瓶颈。
Ben Voigt 2010年

7

不幸的是,我认为您不会找到很多可靠的信息。F#仍然是一种非常新的语言,因此,即使它非常适合于性能繁重的工作负载,仍然不会有很多具有丰富经验的人来报告。此外,很难准确地评估性能,并且很难概括微基准。即使在C ++中,您也可以看到编译器之间的巨大差异-您是否想知道F#是否可以与任何其他产品竞争?C ++编译器或假设的“最佳” C ++可执行文件?

至于针对C ++的特定基准,以下是一些可能相关的链接:O'Caml与F#:QR分解F#与非托管C ++的并行数字。请注意,作为F#相关材料的作者和F#工具的供应商,作者对F#的成功有着既定的兴趣,因此请一these而就。

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

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


3
f#编译器可能是新的(因此F#编译器生成的代码的性能未知),但是F#的面向功能的部分远不是新的。它可以不作任何更改(这仅适用于以特定方式编写的F#)被编译为OCaml,它已经存在了多个世纪。如果F#中的优化器与OCaml优化器处于同等水平,则OCaml可证明是一种对优化器非常友好的语言(由于一种不变性),那么大量运算非常适合F#
Rune FS 2010年

8
@RuneFS-在O'Caml中获得良好的性能通常是以不使用其较高级别的结构为代价的(例如,请参阅janestreetcapital.com/minsky_weeks-jfp_18.pdf的3.3节)。当谈论现实世界中的F#性能时,当前唯一的F#实现在.NET(CLR或Mono)上运行的事实也意味着某些优化可能不可用。我是F#的忠实拥护者,将来进一步的优化可能会提高速度,但是目前我怀疑在许多应用程序中,“最佳” C ++代码的性能将超过“最佳” F#代码。
2010年

1
F#运行足够快。我不希望它的编译器能够大幅度改进。该语言仍然是允许副作用的语言,可保证特定的执行顺序;极大地限制了优化。例如,即使它们在功能世界中在语义上是等效的,例如它们也与F#let f x y = (expensive x |> g) y根本不同let f x = expensive x |> g
伊蒙·纳邦

1
@Eamon-当然有挑战。但是,我认为您的职位过于黯淡。因为F#在CLR上运行,所以对F#编译器本身或CLR JIT的改进都会影响性能。.NET JIT编译器可能在很多地方都可以得到显着改进(例如,跳过各种可证明不必要的数组边界检查,内联启发式改进等)。鉴于这是一个由小型团队创建的语言的第一个生产版本,如果进一步的努力可以改善F#编译器的输出,我也不会感到惊讶。
kvb 2010年

1
纯度注释可能是性能的一大胜利。而且我并不是想贬低F#,只是我在代码简洁性和可读性方面看到了它的更多好处,而不是期望获得许多性能好处。我宁愿人们选择F#,其原因是因为他们认为perf更好-然后在发现很少见的情况下将其丢弃。关于新的和改进的CLR优化:CLR已有10年的历史了。虽然这当然不是完美的,但我不再指望大幅提高性能。明显的改进将已经完成。
伊蒙·纳邦

4

这是我可以分享的两个示例:

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

  2. 轻量级

我有一个使用LBFGS优化的大规模Logistic回归求解器,它是用C ++编码的。该实现进行了很好的调整。我修改了一些代码以C ++ / CLI进行编码,即,将代码编译为.Net。.Net版本比在不同数据集上天真的编译版本慢3至5倍。如果用F#编码LBFGS,则性能不能比C ++ / CLI或C#更好(但是会非常接近)。

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


3
-1:这是不正确的:“如果用F#编写LBFGS,则性能不能比C ++ / CLI或C#更好(但会非常接近)。”。这正是F#可以比C#快很多的应用程序。
JD 2010年

@Jon为什么?你是说“平行”吗?
尹铸2010年

1
@乔 我已经对LBFGS进行了编码,我知道提高性能和内存使用率的技巧必须以命令式方式进行编码。FP在这里似乎具有良好的设计模式,但是性能与样式无关,特别是对于高度优化的数字代码。在使用LBFGS的大多数问题中,时间成本主要集中在函数值和梯度计算中,很少一部分用于LBFGS本身。如果LBFGS或行搜索迭代的次数远多于函数值和梯度的计算,则使其内联确实可以提高性能。但是,这通常是不正确的。
尹铸2010年

1
其次,我看不到直接将向量(数组指针)传递给函数,运行它并返回另一个指向渐变数组的指针的性能问题。如果此功能仅花费很少的时间(如果交互中有一些开销),则内联会有所帮助。因为渐变数组通常尺寸很大(这就是为什么我们需要Limitedmemory-BFGS),所以我们必须确保渐变数组已预先分配并在以后的迭代中可以重用。在这种东西的实现中,只有很多必要的思考。
尹铸2010年

3
不,inlineF#的主要好处不是消除了函数调用的开销,而是使CLR对您的代码进行了类型专用化。如果您的LBFGS仅用于处理float arrayvector输入和输出,则您针对一种特殊情况手工键入了专用字,这使它的用处大大降低。通用BFGS实现应使用用户提供的功能读取其输入并将其输出直接写入用户的数据结构中。F#在这里比C#具有巨大的性能优势。
JD 2010年

3

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

首先,除非您是故意进行一些复杂的递归,而且我猜您自从询问数字以来就没有这种感觉,否则不要指望F#与C#性能有任何区别。

由于CLR并非针对跨平台统一性,因此它在浮点数方面肯定比Java更好,这意味着JIT将尽可能地达到80位。另一方面,除了观察变量的数量以确保有足够的FP寄存器之外,您无法控制其他事情。

从矢量角度讲,如果您大声尖叫,则可能是在2-3年内发生某些事情,因为Direct3D仍将以通用API的身份进入.NET,并且XNA上运行的C#代码在Xbox whihc上运行,与CLR所能获得的裸机非常接近。这仍然意味着您需要自己执行一些中间代码。

因此,不要指望CUDA甚至没有能力链接NVIDIA库并继续前进。如果由于某种原因您真的需要“功能性”语言,因为Haskell出于纯粹的必要性而被设计为友好链接,那么您将有更多的运气尝试使用Haskell。

已经提到了Mono.Simd,虽然应该将其反向移植到CLR,但实际上可能需要做很多工作。

social.msdn中有相当多的代码发布了在.NET中使用SSE3,C ++ / CLI和C#,数组blitting,为perf注入SSE3代码等。

有人谈论在已编译的C#上运行CECIL以将零件提取到HLSL中,编译到着色器中并链接粘合代码以对其进行调度(无论如何,CUDA都在进行等效操作),但是我认为这不会产生任何可运行的结果。

如果您想尽快尝试某些东西,可能会更有价值,这是Codeplex上的PhysX.Net。不要指望它会解压缩并完成魔术。但是,ih目前是活跃的作者,并且代码都是普通的C ++和C ++ / CLI,如果您想详细介绍也许对CUDA使用类似的方法,那么yopu可能会从作者那里得到一些帮助。对于全速CUDA,您仍然需要编译自己的内核,然后仅与.NET进行接口连接,因此该部分越容易变得越快乐。

有一个CUDA.NET库,该库应该是免费的,但是该页面仅提供了电子邮件地址,因此请附加一些字符串,并且在作者撰写博客时,他对库中的内容并没有特别的谈论。

哦,如果您有预算,您可能会喜欢Psi Lambda(KappaCUDAnet是.NET的一部分)。显然,他们将抬高11月份的价格(如果这不是销售技巧:-)


2
模式匹配的优化是F#可以做很多事情而C#却无能为力的领域。这与科学计算中的符号计算有关。并非偶然,某些世界上最大的符号计算是用F#的前身OCaml编写的。
JD 2010年

2

首先,C比C ++快得多。因此,如果您需要如此之高的速度,则应在c中创建lib等。

关于F#,大多数基准标记使用的Mono比MS CLR慢2倍,部分原因是使用boehm GC(它们具有新的GC和LVVM,但它们仍不成熟,不支持泛型等)。

.NEt语言本身可以编译为IR(CIL),后者可以像C ++一样高效地编译为本机代码。大多数GC语言都会遇到一个问题,那就是大量的可变写入(如上所述,这包括C ++ .NET)。并且有一个特定的科学问题集对此要求,这些问题可能需要使用本机库或使用Flyweight模式重用池中的对象(从而减少写入)。原因是.NET CLR中存在写障碍,当更新参考字段(包括框)时,它将在表中设置一个位,表明该表已修改。如果您的代码包含大量此类写操作,则会受到影响。

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

但是,F#的亮点是对不可变数据的并行处理,这种处理与更多基于读取的问题并存。值得注意的是,大多数基准测试在可变写入中都比实际应用程序高得多。

关于浮点,由于它比较慢,因此应该使用oCaml的替代库(即.Net一个)。C / C ++允许更快地降低精度,而oCaml默认情况下不会。

最后,我将争辩说像C#,F#这样的高级语言和适当的配置文件,在相同的开发时间下,您会比c和C ++具有更好的性能。如果您更改瓶颈以进行ac lib pinvoke通话,则对于关键区域,您还将获得类似C的性能。就是说,如果您有无限的预算并且更在乎速度,那么与C相比,维护是行之有效的方法(不是C ++)。


1

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

但是有关“您的里程可能会有所不同”的说法在所有基准测试中都是正确的。一揽子声明(我的除外)很少是真的。


2
抱歉,我完全不理解此评论。
duffymo'5

2
由于惯性,它们中的大多数仍然是fortran(我认为fortran在今天没有太大的优势)。linpack(被lapack取代)也是如此。最近的一些blas实现,例如atlas和goto实际上是C和平台内在函数,而不是fortran。
Anycorn

1
我承认,我的数据已过时。但是我很想看到一个基准,今天比较Fortran和C的线性代数。关键问题是:现代商业包装的供应商使用哪种语言?
duffymo'5

我不知道。我查看了mkl的二进制字符串,它似乎是C和fortran的混合物,更多是fortran。但是我本以为会有一些针对内核的大型手动调试程序集。确实会很有趣。
2010年

1
我们用于数值计算的现代商业软件包是用F#编写的,它非常高兴地击败了Fortran。FFTW在MATLAB中提供了FFT例程,并用OCaml编写,并且使所有其他事情都非常开心。
JD
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.