F#代码优化还是真的那么慢?

The*_*igW 6 .net c++ f#

我正在寻找一种方法,使用.NET进行适当的算法编码,具有现代语言的所有好处(例如,我喜欢强类型检查,运算符重载,lambda,泛型算法).通常我用C++编写算法(主要是图像处理).由于F#作为一种语言似乎很有趣,我玩了一下,但似乎很慢.作为一个最简单的测试我只做了一些数组操作 - >图像的亮度增加:

let r1 = rgbPixels |> Array.map (fun x -> x + byte(10) )
Run Code Online (Sandbox Code Playgroud)

它似乎比比较C++实现至少慢8倍 - 对于更复杂的算法,例如2D卷积,情况更糟.有没有更快的方法或我错过任何特定的编译器设置(是的,建立发布优化...)?我愿意为这个好的和高的抽象付出代价,但是这样的开销并不好(我需要在8个内核上进行并行化以补偿:)) - 至少它会破坏进一步学习的动力......我的其他选择将我的更重的算法留在C++中并与manged C++接口,但这并不好,因为维护托管包装将是一个相当大的负担.

Guv*_*nte 6

如果你担心性能,要记住的一个重要事项是默认情况下F#不会改变任何东西.这需要复制许多天真的算法实现,例如您描述的算法.

编辑:我不知道为什么,但以下代码的简单测试提供了较差的结果Array.map.请务必分析在执行这些优化时尝试的任何算法.但是我for和之间的结果非常相似map.

Array.map为操作的结果创建一个新数组,而不是你想要的Array.iteri.

rgbPixels |> Array.iteri (fun i x -> rgbPixels.[i] <- x + 10uy)
Run Code Online (Sandbox Code Playgroud)

请注意,这可以包含在您自己的模块中,如下所示

module ArrayM =
    let map f a = a |> Array.iteri (fun i x -> a.[i] <- f x)
Run Code Online (Sandbox Code Playgroud)

不幸的是,这是一个必要的恶,因为函数式编程的主要租户之一就是坚持不可变对象,就像算法允许的那样,然后一旦完成,就转换为性能至关重要的突变.如果你知道你的表现从一开始就很重要,你需要从这些帮助者开始.

还要注意,可能有一个库提供了这个功能,我只是不知道它在手边.


kvb*_*kvb 5

我认为可以说,惯用的F#经常无法与优化的C++进行数组操作的性能相匹配,原因如下:

  1. 在.NET中根据数组的边界检查数组访问,以确保内存安全.CLR的JIT编译器能够忽略对某些定型代码的边界检查,但这通常要求您使用带有显式边界的for循环而不是更多惯用的F#构造.
  2. 使用像lambdas(例如fun i -> ...)这样的抽象通常会有少量开销.在紧密循环中,与在循环体中完成的工作相比,这种小的开销最终会变得非常重要.
  3. 据我所知,CLR JIT编译器没有像C++编译器那样利用SSE指令.

在分类帐的另一边,

  1. 在F#代码中永远不会有缓冲区溢出.
  2. 您的代码将更容易推理.作为必然结果,对于给定的总代码复杂度级别,您通常可以在F#中实现比在C++中更复杂的算法.
  3. 如果有必要,您可以编写将以更接近C++的速度运行的单一代码,如果您发现需要编写C++以满足性能要求,还可以使用不安全的C++代码进行互操作.

  • @TheBigW:我非常喜欢F#并且很少建议不要使用它,但它可能不是_every_ job的合适工具. (2认同)