F#在科学计算中的performance
我很好奇F#性能如何与C ++性能相比较? 我问了一个关于Java的类似问题,我得到的印象是Java不适合大数量的编程。
我已经读过F#应该是更具扩展性和更高性能的,但是这个现实世界的性能与C ++相比呢? 有关当前实施的具体问题是:
- 它如何做浮点?
- 它是否允许vector指令?
- 它对优化编译器有多友好?
- 记忆足迹有多大? 它是否允许对内存局部性进行细粒度的控制?
- 它有分布式内存处理器的能力,例如Cray?
- 在涉及大量处理的计算科学中,它有什么特点可能会引起人们的兴趣?
- 是否有实际的科学计算实现使用它?
谢谢
- F#执行的浮点运算速度与.NET CLR允许的一样快。 与C#或其他.NET语言没有多大区别。
- F#本身不允许使用向量指令,但是如果你的CLR有这些API的话,F#不应该有使用它的问题。 参见Mono 。
- 据我所知,目前只有一个F#编译器,所以也许这个问题应该是“F#编译器在优化方面有多优秀?”。 在任何情况下,答案都可能与C#编译器一样好,现在可能稍差一些。 请注意,F#在编译时支持内联,与C#不同,这可能允许依赖于generics的更高效的代码。
- F#程序的内存足迹与其他.NET语言相似。 您对分配和垃圾回收的控制量与其他.NET语言相同。
- 我不知道对分布式内存的支持。
- F#有非常好的处理平面数据结构的基元,例如数组和列表。 查看数组模块的内容:map,map2,mapi,iter,fold,zip …数组在科学计算中很stream行,我猜是由于它们本身具有良好的内存局部性。
- 对于使用F#的科学计算软件包,您可能需要查看Jon Harrop正在做的事情。
我很好奇F#性能如何与C ++性能相比较?
取决于应用而变化很大。 如果您在multithreading程序中广泛使用复杂的数据结构,那么F#可能是一个巨大的胜利。 如果你的大部分时间花在数组循环中,那么C ++可能会快2-3倍。
案例研究:光线跟踪器我的基准testing使用了一棵树来进行分层剔除和数值光线球面交集代码来生成输出图像。 这个基准testing已经有几年的历史了,C ++代码经过几十年的发展已经有了很大的改进,已经有数十万人读过了。 微软的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#实现 ,达到了相同的性能水平。 但是参考实现不是很快:像英特尔的math核心库(MKL)这样的供应商调整的实现速度通常要快10倍。 值得注意的是,我设法优化了我的F#代码,远远超过了英特尔在英特尔硬件上运行的性能,同时保持我的代码低于150行代码,并且是完全通用的(它可以处理单精度和双精度,复杂甚至符号matrix):对于较薄的matrix,我的F#代码比英特尔MKL快3倍。
请注意,这个案例研究的道德并不是你应该期待你的F#比厂商调优的库更快,但是,即使像英特尔这样的专家,如果他们只使用低级语言,也将错过高效的高级优化。 我怀疑英特尔的数值优化专家没有充分利用并行性,因为他们的工具使得它非常麻烦,而F#则使得它毫不费力。
它如何做浮点?
性能类似于ANSI C,但某些function(例如舍入模式)不能从.NET获得。
它是否允许vector指令?
没有。
它对优化编译器有多友好?
这个问题没有任何意义:F#是一个来自Microsoft的专有.NET语言,只有一个编译器。
记忆足迹有多大?
一个空的应用程序在这里使用1.3Mb。
它是否允许对内存局部性进行细粒度的控制?
比大多数内存安全的语言更好,但不如C语言。例如,您可以通过将F#中的任意数据结构表示为“结构体”来取消装箱。
它有分布式内存处理器的能力,例如Cray?
取决于“能力”的含义。 如果你可以在Cray上运行.NET,那么你可以在F#中使用消息传递(就像下一种语言一样),但F#主要用于桌面多核x86机器。
在涉及大量处理的计算科学中,它有什么特点可能会引起人们的兴趣?
内存安全意味着你不会得到分段错误和访问冲突。 在.NET 4中支持并行性是很好的。 在Visual Studio 2010中通过F#交互式会话即时执行代码的能力对于交互式技术计算非常有用。
是否有实际的科学计算实现使用它?
我们在F#中用于科学计算的商业产品已经有数百个用户。
然而,你的问题表明你认为科学计算是高性能计算(例如Cray)而不是交互式技术计算(例如MATLAB,Mathematica)。 F#是为后者devise的。
除了别人说的外,F#还有一个重点,那就是并行 。 普通F#代码的性能是由CLR决定的,尽pipe您可以使用F#中的LAPACK,也可以使用C ++ / CLI作为项目的一部分进行本地调用。
但是,精心devise的function程序往往更容易并行化,这意味着如果您正在进行一些科学计算,则可以使用多核CPU来轻松获得性能。 这里有几个相关的链接:
- F#和任务平行库 (Jurgen van Gael的博客,他在做机器学习的东西)
- 另一个有趣的答案是关于平行
- 从F#使用并行LINQ的一个例子
- 我的书的第14章讨论了并行性( 源代码可用)
关于分布式计算,您可以使用.NET平台提供的任何分布式计算框架。 有一个MPI.NET项目,与F#很好地协作,但是你也可以使用MSR项目DryadLINQ。
- 一些文章: 用于.NET的F#MPI工具 , 与MPI.NET的并发性
- DryadLINQ项目 hompepage
与所有语言/性能比较一样,您的里程在很大程度上取决于您的编码能力。
F#是OCaml的衍生物。 我很惊讶地发现,OCaml在金融界被广泛使用,在这个世界里,数字处理的性能非常重要。 我进一步惊讶地发现OCaml是速度更快的语言之一,其性能与最快的C和C ++编译器相当。
F#build立在CLR上 。 在CLR中,代码以称为通用中间语言的字节码的forms表示。 因此,它受益于JIT的优化function,并且具有与C#(但不一定是C ++)相媲美的性能,如果代码写得很好的话。
通过使用本地图像生成器(NGEN),CIL代码可以在运行之前的单独步骤中编译为本机代码。 这加快了软件的所有后期运行,因为CIL到本地编译不再是必需的。
有一点需要考虑的是像F#这样的函数式语言可以从更具说明性的编程风格中受益。 从某种意义上说,你在命令式语言(如C ++)中过度指定了解决scheme,这限制了编译器的优化能力。 更具说明性的编程风格理论上可以为编译器提供更多的algorithm优化机会。
这取决于你在做什么样的科学计算。
如果你在做traditional heavy computing
,比如线性代数,各种优化,那么你不应该把你的代码放在.Net框架中,至less在F#中是不适合的。 由于这是algorithm级别的,所以大多数algorithm必须以命令式语言编码,以在运行时间和内存使用方面具有良好的性能。 其他人提到并行,我必须说,当你在做一些低级的东西,比如并行的SVD实现时,这可能是没用的。 因为当你知道如何平行SVD时,你根本不会使用高级语言,Fortran,C或者修改后的C(例如cilk )是你的朋友。
然而,现在很多科学计算都不是这种types,这是一些高层次的应用,例如统计计算和数据挖掘。 在这些任务中,除了一些线性代数或优化之外,还有大量的数据stream,IO,预占,做graphics等等。对于这些任务来说,F#确实很强大,因为它的简洁性,function性,安全性,易于平行等
正如其他人所说,.NET很好地支持Platform Invoke,实际上MS里面的很多项目都是使用.Net和P / Invoke在一起来提高性能的瓶颈。
不幸的是,我不认为你会发现很多可靠的信息。 F#仍然是一门非常新的语言,所以即使它非常适合性能繁重的工作负载,仍然不会有那么多具有丰富经验的人员去报告。 此外,性能很难准确测量,微基准很难一概而论。 即使在C ++中,您也可以看到编译器之间的巨大差异 – 您是否想知道F#是否与任何 C ++编译器或者假设的“尽可能好”的C ++可执行文件竞争?
至于针对C ++的特定基准,下面是一些可能相关的链接: O'Caml vs. F#:QR分解 ; F#vs非托pipeC ++的并行数值 。 请注意,作为F#相关资料的作者,作为F#工具的供应商,作者对F#的成功拥有既得利益,因此,请将这些要求放在一个盐度上。
我认为可以肯定地说F#在执行时会有一些应用程序是有竞争力的,而有些应用程序在其他地方可能不是。 在大多数情况下,F#可能需要更多的内存。 当然,最终的performance也将很大程度上取决于程序员的技能 – 我认为F#几乎可以肯定是一个更高效的语言来编程一个适度的程序员。 而且,我认为目前在大多数任务中,Windows上的CLR比单声道在大多数操作系统上的performance要好,这也可能影响你的决定。 当然,由于F#可能比C ++更容易并行化,因此它也将取决于您计划运行的硬件types。
最终,我认为要真正回答这个问题的唯一方法是编写代表您要执行的计算types的F#代码和C ++代码,并对它们进行比较。
以下是我可以分享的两个例子:
-
matrix乘法:我有一篇博客文章比较不同的matrix乘法实现 。
-
LBFGS
我有一个使用LBFGS优化的大规模逻辑回归求解器,用C ++编码。 实施得到了很好的调整。 我修改了一些代码在C ++ / CLI中进行编码,即我将代码编译成.Net。 .Net版本比不同数据集上的天真编译版本慢3到5倍。 如果你在F#中编写LBFGS,性能不会比C ++ / CLI或C#好(但是会非常接近)。
我有另一篇文章, 为什么F#是数据挖掘的语言 ,虽然不是你所关心的性能问题,但与F#中的科学计算相关。
如果我说“在2-3年再次问”我认为这将完全回答你的问题:-)
首先,不要指望F#和C#完全不同,除非你故意做一些令人费解的recursion,而且我猜你不是因为你问了数值。
浮点智能必然要比Java更好,因为CLR并不是针对跨平台的一致性,这意味着JIT将尽可能地达到80位。 另一方面,除了监视variables的数量之外,你不能控制这个variables,以确保有足够的FP寄存器。
Vector-wise,如果你足够大的尖叫,可能会发生2-3年的事情,因为Direct3D作为一个通用的API进入.NET,在XNA上完成的C#代码在Xbox上运行,因为它和CLR一样接近裸机。 这仍然意味着你需要自己做一些中间代码。
所以,不要指望CUDA,甚至不能仅仅连接NVIDIAgraphics库并开始工作。 如果由于某些原因,你真的需要一个“function性”的语言,那么Haskell就会有更多的尝试。
Mono.Simd已经被提及,虽然它应该可以移植到CLR,但实际上可能还需要做一些工作。
在.NET中使用SSE3的social.msdn文件中有相当一部分代码,通过C ++ / CLI和C#来进行数组传输,注入了用于执行的等等的SSE3代码。
有一些关于在编译的C#上运行CECIL的部分,将部分内容提取到HLSL中,编译成着色器,并链接一个胶水代码来安排它(CUDA正在做同样的事情),但我不认为有任何可以运行的东西。
如果你想尽快尝试一些东西,可能对你更有价值,就是在Codeplex上的PhysX.Net 。 不要指望它只是解压缩和做魔术。 但是,ih目前是活跃的作者,代码既是普通的C ++也是C ++ / CLI,如果你想详细说明一下,yopu可以从作者那里得到一些帮助。 对于全速CUDA,你仍然需要编译自己的内核,然后才能连接到.NET,所以越容易越快乐。
有一个CUDA.NET库是应该是免费的,但页面只给出电子邮件地址,所以期望一些string附加,而作者写博客,他不是特别讨论有什么是在里面的lib。
哦,如果你有预算哟可能给Psi Lambda一下(KappaCUDAnet是.NET部分)。 显然他们会在11月份抬高价格(如果这不是一个销售手段:-)
最后我知道,大多数科学计算仍然在FORTRAN中完成。 它比线性代数问题还要快 – 不是Java,不是C,不是C ++,不是C#,不是F#。 LINPACK已经很好的优化了。
但是关于“你的里程可能会有所不同”的评论在所有基准中都是如此。 一揽子报表(我的除外)很less是真的。
首先C比C ++快得多。所以,如果你需要这么多的速度,你应该在C中创buildlib等。
对于F#来说,大多数基准testing使用的单声道比MS CLR慢了2倍,部分原因是它使用了boehm GC(他们有一个新的GC和LVVM,但是这些还不成熟,不支持generics等)。
.NEt语言本身被编译为一个IR(CIL),它可以像C ++那样有效地编译为本地代码。 大多数GC语言都会遇到一个问题,那就是大量的可变写入(包括如上所述的C ++ .NET)。 还有一个科学问题需要这个设置,这些需要的时候可能应该使用一个本地库或者使用Flyweight模式来重用池中的对象(这会减less写入)。 原因是在.NET CLR中有一个写屏障,当更新一个引用字段(包括一个框)时,它会在表中设置一点,说这个表被修改。 如果你的代码包含很多这样的写入,将会受到影响。
也就是说像C#这样的.NET应用程序在结构上使用大量的静态代码,结构和ref / out可以产生类似于C的性能,但是这样编码或维护代码(如C)是非常困难的。
然而,F#闪耀的是对不可变的数据的放肆主义,而这些数据却与更多的基于阅读的问题紧密相关。 值得注意的是大多数的基准testing在可变写入方面比现实生活中的应用要高得多。
关于浮点数,你应该使用一个替代的lib(即.net一个)到oCaml,因为它很慢。 C / C ++允许oCaml在默认情况下更低的精度。
最后,我会争论一个像C#,F#这样的高级语言,适当的性能分析会给你比同一个开发者时间的c和C ++更好的性能。 如果你改变一个瓶颈来交换密码,那么你最终也会得到类似于关键区域的C性能。 这就是说,如果你有无限的预算,关心速度更多,那么维护比C是要走的路(而不是C + +)。