Я пытаюсь изучить статические ограничения элементов в F #. От чтения сообщение в блоге Tomas Petricek, я понимаю, что запись функции inline
, которая "использует только те операции, которые сами записываются с использованием статических ограничений для членов", сделает мою функцию корректной для всех числовых типов, которые удовлетворяют этим ограничениям. Этот вопрос указывает, что inline
работает несколько аналогично шаблонам С++, поэтому я не ожидал разницы в производительности между этими двумя функциями:
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)