F#中片段的可靠时间

时间:2015-12-08 01:13:14

标签: time f# performance-testing

我想知道你是否对如何准确地计算F#中的快速功能有任何建议。我在某个地方找到了这个功能:

let time f =
  let sw = System.Diagnostics.Stopwatch()
  sw.Start()
  let res = f()
  sw.Stop()
  (res, sw.Elapsed.TotalMilliseconds)

仅运行给定函数一次,并返回结果和已用时间。但是当我使用https://repl.it运行它时,它似乎变化很大,这是我目前对F#代码的测试基础。

为了测试一下,我已经为Project Euler 6(link to code)编译了一些不同的替代解决方案:

let square x =
    x * x

let calcDiffSquareSumFromSumSquares list = 
    (list |> List.sum |> square) - (list |> List.sumBy square)


let calcDiffSquareSumFromSumSquaresV2 list = 
    list 
    |> Seq.map (fun x -> (x, x*x) ) 
    |> Seq.reduce (fun (accX, accXX) (elemX, elemXX) -> (accX + elemX, accXX + elemXX))
    |> (fun (x, xx) -> x * x - xx)

let calcDiffSquareSumFromSumSquaresV3 n = 
    seq { 1 .. n } 
    |> Seq.map (fun x -> (x, x*x) ) 
    |> Seq.reduce (fun (accX, accXX) (elemX, elemXX) -> (accX + elemX, accXX + elemXX))
    |> (fun (x, xx) -> x * x - xx)

let calcDiffSquareSumFromSumSquaresV4 n = 
    let sumN = n * (n+1) / 2
    let sumNN = n * (n + 1) * (2*n + 1) / 6
    sumN * sumN - sumNN


let n = 100
let myList = [ 1 .. n ]
printfn "Solution to Project Euler 6:"
printfn "  org: %i" (calcDiffSquareSumFromSumSquares myList)
printfn "  v2 : %i" (calcDiffSquareSumFromSumSquaresV2 myList)
printfn "  v3 : %i" (calcDiffSquareSumFromSumSquaresV3 n)
printfn "  v4 : %i" (calcDiffSquareSumFromSumSquaresV4 n)

(** My timing function ************************************************)

let time f =
  let sw = System.Diagnostics.Stopwatch()
  sw.Start()
  let res = f()
  sw.Stop()
  (res, sw.Elapsed.TotalMilliseconds)


let noopFunc() = List.sum [ 1 .. 1000 ]
let euler6org() = calcDiffSquareSumFromSumSquares myList
let euler6v2() = calcDiffSquareSumFromSumSquaresV2 myList
let euler6v3() = calcDiffSquareSumFromSumSquaresV3 n
let euler6v4() = calcDiffSquareSumFromSumSquaresV4 n

let noop = time noopFunc
let org = time euler6org
let v2 = time euler6v2
let v3 = time euler6v3
let v4 = time euler6v4


printfn "Solution to Project Euler 6:"
printfn "  org: %i in %f ms" (fst org) (snd org)
printfn "  v2 : %i in %f ms" (fst v2) (snd v2)
printfn "  v3 : %i in %f ms" (fst v3) (snd v3)
printfn "  v4 : %i in %f ms" (fst v4) (snd v4)

当我在这里运行时,我得到(仅列出时序输出):

Solution to Project Euler 6:
  org: 25164150 in 0.037000 ms
  v2 : 25164150 in 0.092800 ms
  v3 : 25164150 in 0.049400 ms
  v4 : 25164150 in 0.018700 ms

第二次运行给出:

Solution to Project Euler 6:
  org: 25164150 in 0.034400 ms
  v2 : 25164150 in 0.102300 ms
  v3 : 25164150 in 0.056700 ms
  v4 : 25164150 in 0.016200 ms

刚才这些似乎有些一致,但是如果我改变我先运行的测试的顺序,那么它可以将时序改变几个数量级。因此,请帮助我找到更好或更准确的F#中较小代码片段的计时执行方式(希望在https://repl.it)。

对函数进行计时的最重要方面是尽可能地获得不同函数的运行时间之间的相对时间。我知道并接受在不同平台上运行任何给定功能的确切时间会有所不同。

1 个答案:

答案 0 :(得分:0)

基准测试不是一项简单的任务。 Visual Studio确实有Visual Profiler,Visual Studio 2015社区免费提供。我发现它非常有用。另一个好处是你可以运行你的程序并与它交互,看看各种代码被调用的频率,它调用的是什么以及它在执行时的平均时间。

以下是如何开始使用它的链接:https://msdn.microsoft.com/en-us/library/ms182372.aspx

在针对示例F#控制台应用程序运行它之后,这是一个图像: enter image description here

您需要注意的事项是由cpu完成的分支优化以及您在测试中遇到的任何重复计算,而不是正常代码执行流程的一部分。

以下是示例应用程序的代码:

// define the square function
let square x = x * x

// define the sumOfSquares function
let sumOfSquares n = 
   [1..n] |> List.map square |> List.sum

[<EntryPoint>]
let main argv = 
    for i in 1..100 do
        let s = sumOfSquares i
        printfn "%d" s
    0 // return an integer exit code