F#社区的常识是,PowerPack的报价编译工具会生成非常慢速代码,事实上它实际上比天真解释更糟糕。我一直在研究这个原因,但到目前为止我还没有找到令人信服的答案。有人声称这种情况要么是因为报价中的模式匹配等事物的低效表示,要么是因为库使用表达式树的固有效率低下。我想说明为什么我认为两个都不是一个简单的例子:
#r "FSharp.Powerpack.Linq.dll"
open System
open System.Linq.Expressions
open Microsoft.FSharp.Quotations.Patterns
let powerpack = Microsoft.FSharp.Linq.QuotationEvaluator.Compile <@ 1 + 1 @>
// explicitly rewrite above quotation with expression trees
let expressionTree =
let (Call(_,addM,_)) = <@ 1 + 1 @>
let constExpr (x : 'T) = Expression.Constant(box x, typeof<'T>)
let eval = Expression.Call(addM, constExpr 1, constExpr 1)
let lambda = Expression.Lambda<Func<int>>(eval)
lambda.Compile()
// reflection - based evaluation
let reflection =
let (Call(_,addM,_)) = <@ 1 + 1 @>
fun () -> addM.Invoke(null, [| 1 :> obj ; 1 :> obj |]) :?> int
#time
// QuotationEvaluator ~ 2.5 secs
for i in 1 .. 1000000 do
powerpack () |> ignore
// native evaluation ~ 1 msec
for i in 1 .. 1000000 do
(fun () -> 1 + 1) () |> ignore
// reflection evaluation ~ 700 msec
for i in 1 .. 1000000 do
reflection () |> ignore
// naive expression tree ~ 19 msec
for i in 1 .. 1000000 do
expressionTree.Invoke () |> ignore
这里显然出现了问题。问题是,什么?
编辑:FSharpx.Linq编译器也会出现相同的行为
答案 0 :(得分:2)
以下是编译的实现:
let CompileImpl (e: #Expr, eraseEquality) =
let ty = e.Type
let e = Expr.NewDelegate(GetFuncType([|typeof<unit>; ty |]), [new Var("unit",typeof<unit>)],e)
let linqExpr = Conv (e,eraseEquality)
let linqExpr = (linqExpr :?> LambdaExpression)
let d = linqExpr.Compile()
(fun () ->
try
d.DynamicInvoke [| box () |]
with :? System.Reflection.TargetInvocationException as exn ->
raise exn.InnerException)
注意在委托上使用DynamicInvoke
,这比Invoke
要慢得多,并且是你获得结果的原因。