Вопрос производительности F #: что делает компилятор? - PullRequest
4 голосов
/ 01 июня 2010

Ссылка на этот код: 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 Ответ

1 голос
/ 01 июня 2010

Отражатель показывает, что 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 Debug
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
...