2010-05-31 3 views
4

Реферирование этот код: F# Static Member Type ConstraintsF # вопрос производительности: что делает компилятор?

Почему, например,

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) 

Глядя на отражатель, я могу видеть, что компилятор обрабатывает каждый из них по-разному, но для меня слишком много делается для того, чтобы расшифровать существенную разницу. Наивно я предполагал, что первое будет работать лучше, чем позже, потому что gL предварительно вычисляется, тогда как G_of 1L должен быть вычислен 100 000 раз (по крайней мере, так выглядит).

[Edit]

Похоже, что это может быть ошибка с F/.NET 2.0/Release режиме # 2.0 см @ ответ gradbot и обсуждение.

+0

Интересно. Для меня нет смысла - насколько я могу судить, генерируемый IL соответствует вашим ожиданиям - 'gL' предварительно вычисляется в первом случае, но остальная часть кода в значительной степени эквивалентна. Возможно, есть какая-то x64 или JIT-магия? Я попробовал это на 64-битном .NET 2.0 и получил аналогичные результаты (ну, первый из них был 10 и второй 7). –

+0

Спасибо, что посмотрели, я знал, что это было любопытно на лице, но понял, что существует достаточно интересного материала, связанного со статическими ограничениями типа, что вещи не могут быть скомпилированы так, как можно было бы ожидать - интересное предложение, что это может быть связано с JIT " магия». Я получил те же номера на 32-битном .NET 2.0. –

+0

Я предполагаю, что это связано с тем, что первая версия является закрытием, а вторая - анонимной. F # должен создать какой-то анонимный объект для закрытия. – 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 @ 3368Mhz, окна 7 64bit, VS2010 F # 2.0

x86 Debug
test1 8216ms
test2 8237ms

x86 Выпуск
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 
+0

в вопросе, test1() медленнее, а не test2(). –

+0

@Yin: это правильно. @gradbot: очень интересное наблюдение за различной компиляцией, основанной на режиме отладки и релиза ... наблюдали ли вы какие-либо различия в производительности в зависимости от режима? –

+0

Я испытал тот же самый крах в Reflector. –

Смежные вопросы