我正在尝试学习 F# 中的静态成员约束。通过阅读Tomas Petricek 的博客文章,我了解到编写一个inline
“仅使用本身使用静态成员约束编写的操作”的函数将使我的函数对于满足这些约束的所有数字类型都能正常工作。这个问题表明它的inline
工作方式与 c++ 模板有些相似,所以我没想到这两个函数之间有任何性能差异:
let MultiplyTyped (A : double[,]) (B : double[,]) =
let rA, cA = (Array2D.length1 A) - 1, (Array2D.length2 A) - 1
let cB = (Array2D.length2 B) - 1
let C = Array2D.zeroCreate<double> (Array2D.length1 A) (Array2D.length2 B)
for i = 0 to rA do
for k = 0 to cA do
for j = 0 to cB do
C.[i,j] <- C.[i,j] + A.[i,k] * B.[k,j]
C
let inline MultiplyGeneric (A : 'T[,]) (B : 'T[,]) =
let rA, cA = Array2D.length1 A - 1, Array2D.length2 A - 1
let cB = Array2D.length2 B - 1
let C = Array2D.zeroCreate<'T> (Array2D.length1 A) (Array2D.length2 B)
for i = 0 to rA do
for k = 0 to cA do
for j = 0 to cB do
C.[i,j] <- C.[i,j] + A.[i,k] * B.[k,j]
C
然而,将两个 1024 x 1024 矩阵相乘,MultiplyTyped
在我的机器上平均需要 2550 毫秒,而MultiplyGeneric
大约需要 5150 毫秒。我最初认为这zeroCreate
是通用版本的错误,但是将该行更改为下面的行并没有什么不同。
let C = Array2D.init<'T> (Array2D.length1 A) (Array2D.length2 B) (fun i j -> LanguagePrimitives.GenericZero)
有什么我在这里想念的东西来做MultiplyGeneric
一样的MultiplyTyped
吗?或者这是预期的?
编辑:我应该提一下,这是 VS2010,F# 2.0,Win7 64bit,发布版本。平台目标是 x64(用于测试更大的矩阵) - 这会有所不同:x86 为这两个函数产生相似的结果。
奖励问题:推断的类型MultiplyGeneric
如下:
val inline MultiplyGeneric :
^T [,] -> ^T [,] -> ^T [,]
when ( ^T or ^a) : (static member ( + ) : ^T * ^a -> ^T) and
^T : (static member ( * ) : ^T * ^T -> ^a)
^a
类型从何而来?
编辑 2:这是我的测试代码:
let r = new System.Random()
let A = Array2D.init 1024 1024 (fun i j -> r.NextDouble())
let B = Array2D.init 1024 1024 (fun i j -> r.NextDouble())
let test f =
let sw = System.Diagnostics.Stopwatch.StartNew()
f() |> ignore
sw.Stop()
printfn "%A" sw.ElapsedMilliseconds
for i = 1 to 5 do
test (fun () -> MultiplyTyped A B)
for i = 1 to 5 do
test (fun () -> MultiplyGeneric A B)