F#性能问题:什么是编译器做? [英] F# performance question: what is the compiler doing?

查看:165
本文介绍了F#性能问题:什么是编译器做?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

引用此代码: F#静态成员类型限制

为什么是

  let gL = G_of 1L 
[1L..100000L] |>显着低于

$的列表.map(fun n - > factorize gL n)



b
$ b

  [1L..100000L] |> List.map(fun n  - > factorize(G_of 1L)n)

我可以看到,编译器正在以非常不同的方式处理每一个,但是有太多的事情要让我破译本质区别。我认为前者的性能比后者好,因为gL是预先计算的,而G_of1L必须计算100,000次(至少看起来是这样的)。



strong> Edit> ]



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

解决方案

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

  let test1 b $ b 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)

/ p>

  let sw = Stopwatch.StartNew()
test1()|>忽略
sw.Stop()
Console.WriteLine(test1 {0} ms,sw.ElapsedMilliseconds)

让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

让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目标)
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,那么一个其他的零一个

让rec获取ig =
如果i = x然后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
}

让内联G_of(目标:'a):(G )= {
零=零目标
一=目标
two = two_of target
three = three_of target
negone = negone_of target
any = any_of target
}

让inline factorizeG n =
let g = G_of n
让rec因式分解nj flist =
如果n = g.one然后flist
elif n%j = g.zero then factorize(n / j) j(j :: flist)
else factorize n(j + g.one)(flist)
因式分解n g.two []

允许inline factorize ;'a>)n = //'
let rec factorize nj flist =
如果n = g.one然后flist
elif n%j = g.zero then factorize )j(j :: flist)
else factorize n(j + g.one)(flist)
因式分解n g.two []

让test1 b $ b 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()忽略
sw2.Stop()
Console.WriteLine(test1 {0} ms,sw2.ElapsedMilliseconds)

让sw = Stopwatch.StartNew()
test2()|> ignore
sw.Stop()
Console.WriteLine(test2 {0} ms,sw.ElapsedMilliseconds)

Console.ReadLine()|>忽略


Referencing this code: F# Static Member Type Constraints

Why is, for example,

let gL = G_of 1L
[1L..100000L] |> List.map (fun n -> factorize gL n)

significantly slower than

[1L..100000L] |> List.map (fun n -> factorize (G_of 1L) n)

By looking at Reflector, I can see that the compiler is treating each of these in very different ways, but there is too much going on for me to decipher the essential difference. Naively I assumed the former would perform better than the later because gL is precomputed whereas G_of 1L has to be computed 100,000 times (at least it appears that way).

[Edit]

It looks like this may be a bug with F# 2.0 / .NET 2.0 / Release-mode, see @gradbot's answer and discussion.

解决方案

Reflector shows test2() turned into 4 classes while test1() is turned into two classes. This only happens in debug mode. Reflector shows identical code (one class for each) in release mode. Unfortunately Reflector is crashing when I try to view the source in C# and the IL is really long.

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)

A quick benchmark.

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)

Benchmarks ran on I7 950 @3368Mhz, windows 7 64bit, VS2010 F#2.0

x86 Debug
test1 8216ms
test2 8237ms

x86 Release
test1 6654ms
test2 6680ms

x64 Debug
test1 10304ms
test2 10348ms

x64 Release
test1 8858ms
test2 8977ms

Here is the complete code.

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

这篇关于F#性能问题:什么是编译器做?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆