是否应该将struct tuples作为memoized函数的参数?

时间:2018-06-14 01:58:07

标签: performance reference f# tuples memoization

在F#元组中是引用类型。 F#还有结构元组,它们是值类型。使用struct元组有速度优势。这在以下代码中进行了演示。

与采用参考元组相比,可能有一个原因使得记忆函数的性能降低,这些函数以结构元组作为参数。 memoized函数必须检查字典中是否存在键。如果键是引用类型,则应该是简单的引用相等比较。另一方面,如果键是值类型并且是重的"对象然后结构平等比较可能会很慢。这在下面的例子中没有发生,但似乎是我无法生成的理论上的可能性。

问题:如果"重"将元组作为参数传递给memoized函数最好让函数接受标准(引用)元组作为参数而不是struct tuple?如果是,那么上面段落中的推理是否是对速度差的正确解释?

open System.Collections.Concurrent

let inline memoizeConcurrent f =
    let dict = ConcurrentDictionary()
    fun x -> dict.GetOrAdd(Some x, lazy (f x)).Force()

let time (msg: string) (f: 'S->'T) (x: 'S) : 'T =
    let stopWatch = System.Diagnostics.Stopwatch.StartNew()

    let f_x = f x

    let ms = stopWatch.Elapsed.TotalMilliseconds

    let msg' =
        if ms < 10000.0 then sprintf " - Elapsed time: %f ms" ms
        else sprintf " - Elapsed time: %f seconds" (ms / 1000.0)

    printfn "%s" (msg + msg')

    f_x

let xs = [0.0..10000.0]
let ys = xs |> List.map (fun x -> x + 1.0)

let tup : list<float>*list<float> = (xs, ys)
let tupStruct : struct (list<float>*list<float>) = struct (xs, ys)

let n = 100 // other values of n will be tried

let foo ((x,y): list<float>*list<float>) : unit =
    [1..n]
    |> List.iter (fun _ -> List.map2 (+) x y |> List.average |> ignore)

let fooStruct (struct(x, y): struct (list<float>*list<float>)) : unit =
    [1..n]
    |> List.iter (fun _ -> List.map2 (+) x y |> List.average |> ignore)


let fooMemo : list<float>*list<float>->unit =
    memoizeConcurrent foo

let fooStructMemo : struct (list<float>*list<float>)->unit =
    memoizeConcurrent fooStruct

time "foo" foo tup
time "fooMemo1" fooMemo tup
time "fooMemo2" fooMemo tup
printfn "\n"
time "fooStruct" fooStruct tupStruct
time "fooStructMemo1" fooStructMemo tupStruct
time "fooStructMemo2" fooStructMemo tupStruct

// ----------------- n = 1 ----------------

foo - Elapsed time: 16.947700 ms
fooMemo1 - Elapsed time: 27.495900 ms
fooMemo2 - Elapsed time: 5.575100 ms


fooStruct - Elapsed time: 1.942500 ms
fooStructMemo1 - Elapsed time: 2.866500 ms
fooStructMemo2 - Elapsed time: 5.978400 ms

// ----------------- n = 10 ----------------

foo - Elapsed time: 12.382000 ms
fooMemo1 - Elapsed time: 32.707500 ms
fooMemo2 - Elapsed time: 5.172900 ms


fooStruct - Elapsed time: 4.746200 ms
fooStructMemo1 - Elapsed time: 7.651900 ms
fooStructMemo2 - Elapsed time: 2.936400 ms


// ----------------- n = 100 ----------------
foo - Elapsed time: 44.160700 ms
fooMemo1 - Elapsed time: 48.957600 ms
fooMemo2 - Elapsed time: 5.695200 ms


fooStruct - Elapsed time: 20.628500 ms
fooStructMemo1 - Elapsed time: 25.449000 ms
fooStructMemo2 - Elapsed time: 3.104300 ms

// ----------------- n = 100000 ----------------

foo - Elapsed time: 20.120753 seconds
fooMemo1 - Elapsed time: 18.981092 seconds
fooMemo2 - Elapsed time: 5.362500 ms


fooStruct - Elapsed time: 18.776220 seconds
fooStructMemo1 - Elapsed time: 19.005812 seconds
fooStructMemo2 - Elapsed time: 2.761800 ms

一个难题:当n = 1时计算fooStructMemo2需要5 ms,而当n具有其他值时,只需2或3 ms。这不是随机效应。差异是一致的。

0 个答案:

没有答案