F# で静的メンバー制約を学習しようとしています。Tomas Petricek のブログ投稿を読んinline
で、「静的メンバー制約を使用して記述された操作のみを使用する」関数を記述すると、それらの制約を満たすすべての数値型に対して関数が正しく機能することがわかりました。この質問は、C++ テンプレートと同様に機能することを示してinline
いるため、これら 2 つの関数のパフォーマンスの違いは予想していませんでした。
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
それにもかかわらず、2 つの 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 64 ビット、リリース ビルドであることに注意してください。プラットフォーム ターゲットは x64 (より大きなマトリックスをテストするため) です。これが違いを生みます。x86 は、2 つの関数に対して同様の結果を生成します。
おまけの質問: 推論される型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)