为什么减少比sum或sumBy更快?

dev*_*rts 13 f#

我的同事和我正在比较传递lambda来做工作时C#函数的速度,而内联函数与工作时间有关.我们发现,在将lambda投影传递给C#select函数时(例如),您想要查看F#是否存在相同的问题,或者它是否有不同之处.

无论我们的初衷是什么,我们偶然发现了一些我们无法弄清楚的事情.在下面的例子中,我们总结了3种不同的方式

  1. 降低
  2. SumBy

module fs

open NUnit.Framework
open FsUnit
open System
open System.Diagnostics;

[<Test>]
let sumTest() = 
    let nums = [0..1000]

    let repeat = 100000

    let stopWatch = new Stopwatch()

    stopWatch.Start()

    let sumsReduce = 
        [
            for i in [0..repeat] do
                yield List.reduce (+) nums
        ]

    Console.WriteLine("reduce = {0} - Time = {1}", List.head sumsReduce, stopWatch.Elapsed.TotalSeconds); 
    stopWatch.Restart()

    let sumsSum = 
        [
            for i in [0..repeat] do
                yield List.sum nums
        ]

    Console.WriteLine("sum = {0} - Time = {1}", List.head sumsSum, stopWatch.Elapsed.TotalSeconds); 
    stopWatch.Restart()


    let sumsSumBy = 
        [
            for i in [0..repeat] do
                yield List.sumBy id nums
        ]

    Console.WriteLine("sumBy = {0} - Time = {1}", List.head sumsSumBy, stopWatch.Elapsed.TotalSeconds); 
    stopWatch.Restart()
Run Code Online (Sandbox Code Playgroud)

输出到这个看起来像:

reduce = 500500 - Time = 0.2725156
sum = 500500 - Time = 1.1183165
sumBy = 500500 - Time = 1.1126781
Run Code Online (Sandbox Code Playgroud)

所以显然减少是这里的大赢家.在反编译中,我可以看到reduce降低了

[Serializable]
internal class sumsReduce\u004021\u002D1 : OptimizedClosures.FSharpFunc<int, int, int>
{
  internal sumsReduce\u004021\u002D1()
  {
    base.\u002Ector();
  }

  public override int Invoke(int x, int y)
  {
    return x + y;
  }
}
Run Code Online (Sandbox Code Playgroud)

但是我很难搞清楚总和和sum在做什么.时间差异在哪里?


目前的答案表明,降低速度要快5倍,因为最初我正在减少一个未经检查的运算符.但是,更新测试以使用已检查的运算符(来自Checked模块),我仍然得到相同的结果

let sumsReduce = 
        [
            for i in [0..repeat] do
                yield List.reduce (Checked.(+)) nums
        ]
Run Code Online (Sandbox Code Playgroud)

请注意,时间差异仍然存在

reduce = 500500 - Time = 0.274697
sum = 500500 - Time = 1.1126796
sumBy = 500500 - Time = 1.1370642
Run Code Online (Sandbox Code Playgroud)

7sh*_*rp9 17

Sum和SumBy使用枚举器:

    while e.MoveNext() do
        acc <- Checked.(+) acc e.Current
    acc
Run Code Online (Sandbox Code Playgroud)

而reduce使用带有优化闭包的递归循环:(减少使用盖子下面的折叠 - 折叠f头尾)

    let fold<'T,'State> f (s:'State) (list: 'T list) = 
        match list with 
        | [] -> s
        | _ -> 
            let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt(f)
            let rec loop s xs = 
                match xs with 
                | [] -> s
                | h::t -> loop (f.Invoke(s,h)) t
            loop s list
Run Code Online (Sandbox Code Playgroud)

使用优化的闭包通常可以提高性能.


ild*_*arn 7

sumsumBy使用检查算术,但你将未经检查的算子传递+reduce- 不完全是苹果到苹果.

  • 好的,所以我做了一个新的测试,我认为不是这样。我要更新问题 (2认同)