F# 性能问题:编译器在做什么?

2023-11-30

参考这段代码: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 次(至少看起来是这样)。

[Edit]

看来这可能是 F# 2.0 / .NET 2.0 / Release-mode 的错误,请参阅@gradbot 的回答和讨论。


Reflector 显示 test2() 变成了 4 个类,而 test1() 变成了两个类。这仅发生在调试模式下。 Reflector 在发布模式下显示相同的代码(每个类一个类)。不幸的是,当我尝试查看 C# 源代码并且 IL 非常长时,Reflector 崩溃了。

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、Windows 7 64 位、VS2010 F#2.0 上运行

x86 调试
test1 8216ms
test2 8237ms

x86 版本
test1 6654ms
test2 6680ms

x64 调试
test1 10304ms
test2 10348ms

x64 版本
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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

F# 性能问题:编译器在做什么? 的相关文章

随机推荐