Вопрос производительности F#: что делает компилятор?
Ссылка на этот код: F# Статический член Тип Ограничения
Почему, например,
let gL = G_of 1L
[1L..100000L] |> List.map (fun n -> factorize gL n)
значительно медленнее, чем
[1L..100000L] |> List.map (fun n -> factorize (G_of 1L) n)
Глядя на Reflector, я вижу, что компилятор обрабатывает каждый из них совершенно по-разному, но я слишком много делаю, чтобы понять существенную разницу. Наивно я предполагал, что первый будет работать лучше, чем последний, потому что gL рассчитывается заранее, тогда как G_of 1L должен вычисляться 100 000 раз (по крайней мере, так кажется).
[Изменить]
Похоже, что это может быть ошибкой в F# 2.0 / .NET 2.0 / Release-mode, см. Ответ и обсуждение @gradbot.
1 ответ
Отражатель показывает, что test2() превратился в 4 класса, а test1() - в два класса. Это происходит только в режиме отладки. Отражатель показывает идентичный код (один класс для каждого) в режиме выпуска. К сожалению, Reflector дает сбой, когда я пытаюсь просмотреть исходный код в C#, и IL очень длинный.
let test1() =
let gL = G_of 1L
[1L..1000000L] |> List.map (fun n -> factorize gL n)
let test2() =
[1L..1000000L] |> List.map (fun n -> factorize (G_of 1L) n)
Быстрый тест.
let sw = Stopwatch.StartNew()
test1() |> ignore
sw.Stop()
Console.WriteLine("test1 {0}ms", sw.ElapsedMilliseconds)
let sw2 = Stopwatch.StartNew()
test2() |> ignore
sw2.Stop()
Console.WriteLine("test2 {0}ms", sw2.ElapsedMilliseconds)
Тесты выполнялись на I7 950 @3368 МГц, windows 7 64 бит, VS2010 F#2.0
Отладка x86test1 8216ms
test2 8237ms
x86 Releasetest1 6654ms
test2 6680ms
отладка x64test1 10304ms
test2 10348ms
x64 Releasetest1 8858ms
test2 8977ms
Вот полный код.
open System
open System.Diagnostics
let inline zero_of (target:'a) : 'a = LanguagePrimitives.GenericZero<'a>
let inline one_of (target:'a) : 'a = LanguagePrimitives.GenericOne<'a>
let inline two_of (target:'a) : 'a = one_of(target) + one_of(target)
let inline three_of (target:'a) : 'a = two_of(target) + one_of(target)
let inline negone_of (target:'a) : 'a = zero_of(target) - one_of(target)
let inline any_of (target:'a) (x:int) : 'a =
let one:'a = one_of target
let zero:'a = zero_of target
let xu = if x > 0 then 1 else -1
let gu:'a = if x > 0 then one else zero-one
let rec get i g =
if i = x then g
else get (i+xu) (g+gu)
get 0 zero
type G<'a> = {
negone:'a
zero:'a
one:'a
two:'a
three:'a
any: int -> 'a
}
let inline G_of (target:'a) : (G<'a>) = {
zero = zero_of target
one = one_of target
two = two_of target
three = three_of target
negone = negone_of target
any = any_of target
}
let inline factorizeG n =
let g = G_of n
let rec factorize n j flist =
if n = g.one then flist
elif n % j = g.zero then factorize (n/j) j (j::flist)
else factorize n (j + g.one) (flist)
factorize n g.two []
let inline factorize (g:G<'a>) n = //'
let rec factorize n j flist =
if n = g.one then flist
elif n % j = g.zero then factorize (n/j) j (j::flist)
else factorize n (j + g.one) (flist)
factorize n g.two []
let test1() =
let gL = G_of 1L
[1L..100000L] |> List.map (fun n -> factorize gL n)
let test2() =
[1L..100000L] |> List.map (fun n -> factorize (G_of 1L) n)
let sw2 = Stopwatch.StartNew()
test1() |> ignore
sw2.Stop()
Console.WriteLine("test1 {0}ms", sw2.ElapsedMilliseconds)
let sw = Stopwatch.StartNew()
test2() |> ignore
sw.Stop()
Console.WriteLine("test2 {0}ms", sw.ElapsedMilliseconds)
Console.ReadLine() |> ignore