在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。这不是随机效应。差异是一致的。