Вопрос производительности 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

Отладка x86
test1 8216ms
test2 8237ms

x86 Release
test1 6654ms
test2 6680ms

отладка x64
test1 10304ms
test2 10348ms

x64 Release
test1 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
Другие вопросы по тегам