最佳绩效评估员f#

时间:2011-06-22 09:33:55

标签: performance f# evaluator

我已经实现了一些函数的天真评估器。我刚刚进入f#的世界,所以它会问你存在(如果是的话,如何实现)更快的评估者。我必须得到一个包含(variableName,(condition + newVariableName))的数据结构。这两个字段是字符串。以下是我的评估者版本的一部分:

let env = new Dictionary<_,_>(HashIdentity.Structural)
//eval: expr -> Prop
let rec eval = function
//Val of string -- is a variable name or a string value
| Val e -> if e.Equals("TRUE") then True       // True is a Prop type used for BDD Algorithm
           elif e.Equals("FALSE") then False   // False is a Prop type used for BDD Algorithm
           else var e    //var of string --- is a Prop type used for BDD Algorithm
| Int i -> var (i.ToString())
| Float e -> var (e.ToString()) 
| Const c -> var c //Const of string --- is a constant name (ex. "$foo" is a constant)
| Path (e, s) -> var ((eval e).ToString() + "." + s)
| Lookup(s, el) -> var s
| Integer(ex) -> eval ex 
| FromTo (e, el) ->var ((eval e).ToString() + (eval el.Head).ToString() + (eval el.Tail.Head).ToString())
| Str(e) -> eval e
| Equality (v, e) -> let evalV = eval  v
                     let evalE = eval  e
                     match evalE with
                     | Var e -> 
                            let sndKey = "Equality" + evalE.ToString()
                            if env.ContainsKey (evalV.ToString()) then 
                                if env.[(evalV.ToString())].Equals(sndKey) then
                                    var env.[(evalV.ToString())]
                                else ~~~ (var env.[(evalV.ToString())]) 
                            else
                                env.Add((evalV.ToString()), (evalV.ToString()) + sndKey)
                                var ((evalV.ToString()) + sndKey)
                     | _ as k -> if bddBuilder.Equiv k False then
                                    let sndKey = "Equality" + "False"
                                    if env.ContainsKey (evalV.ToString()) then 
                                         if env.[(evalV.ToString())].Equals(sndKey) then
                                             var env.[(evalV.ToString())]
                                         else ~~~ (var env.[(evalV.ToString())]) 
                                    else
                                        env.Add((evalV.ToString()), (evalV.ToString()) + sndKey)
                                        var ((evalV.ToString()) + sndKey)
                                 else let sndKey = "Equality" + "True"
                                      if env.ContainsKey (evalV.ToString()) then 
                                          if env.[(evalV.ToString())].Equals(sndKey) then
                                                var env.[(evalV.ToString())]
                                          else ~~~ (var env.[(evalV.ToString())]) 
                                      else
                                          env.Add((evalV.ToString()), (evalV.ToString()) + sndKey)
                                          var ((evalV.ToString()) + sndKey)
| Inequality (v, e) -> let evaluatedV = (eval  v).ToString()
                       let evaluatedE = (eval  e).ToString()
                       let sndKey = "Inequality" + evaluatedE
                       if env.ContainsKey (evaluatedV.ToString()) then 
                            if env.[(evaluatedV.ToString())].Equals(sndKey) then
                                   var env.[(evaluatedV.ToString())]
                            else ~~~ (var env.[(evaluatedV.ToString())]) 
                       else env.Add((evaluatedV.ToString()), (evaluatedV.ToString()) + sndKey)
                            var ((evaluatedV.ToString()) + sndKey)
| IfThenElse (e1, e2, e3) -> (eval e1 &&& eval e2) ||| ((~~~ (eval e1)) &&& eval e3)
| FindString(e1, e2) -> var ("FS" + (eval e1).ToString() + (eval e2).ToString())
| GreaterThan(e1, e2) -> let evaluatedV = (eval  e1).ToString()
                         let evaluatedE = (eval  e2).ToString()
                         let sndKey = "GreaterThan" + evaluatedE
                         if env.ContainsKey (evaluatedV.ToString()) then 
                             if env.[(evaluatedV.ToString())].Equals(sndKey) then
                                    var env.[(evaluatedV.ToString())]
                             else ~~~ (var env.[(evaluatedV.ToString())]) 
                         else env.Add((evaluatedV.ToString()), (evaluatedV.ToString()) + sndKey)
                              var ((evaluatedV.ToString()) + sndKey)
| GreaterThanOrEqual(e1, e2) -> let evaluatedV = (eval  e1).ToString()
                                let evaluatedE = (eval  e2).ToString()
                                let sndKey = "GreaterThanOrEqual" + evaluatedE
                                if env.ContainsKey (evaluatedV.ToString()) then 
                                    if env.[(evaluatedV.ToString())].Equals(sndKey) then
                                        var env.[(evaluatedV.ToString())]
                                    else ~~~ (var env.[(evaluatedV.ToString())]) 
                                else env.Add((evaluatedV.ToString()), (evaluatedV.ToString()) + sndKey)
                                     var ((evaluatedV.ToString()) + sndKey)
| Null(e) -> var ("Null" + (eval e).ToString())
| GetToken(e1, e2, e3) -> var ((eval e1).ToString() + (eval e2).ToString())
| Mod(e1, e2) -> var ((eval e1).ToString() + (eval e2).ToString())
| Match(e1, e2) -> let evaluatedV = (eval  e1).ToString()
                   let evaluatedE = (eval  e2).ToString()
                   let sndKey = "Match" + evaluatedE
                   if env.ContainsKey (evaluatedV.ToString()) then 
                        if env.[(evaluatedV.ToString())].Equals(sndKey) then
                               var env.[(evaluatedV.ToString())]
                        else ~~~ (var env.[(evaluatedV.ToString())]) 
                   else env.Add((evaluatedV.ToString()), (evaluatedV.ToString()) + sndKey)
                        var ((evaluatedV.ToString()) + sndKey)
| LessThenOrEqual(e1, e2) -> let evaluatedV = (eval  e1).ToString()
                             let evaluatedE = (eval  e2).ToString()
                             let sndKey = "LessThen" + evaluatedE
                             if env.ContainsKey (evaluatedV.ToString()) then 
                                if env.[(evaluatedV.ToString())].Equals(sndKey) then
                                    var env.[(evaluatedV.ToString())]
                                else ~~~ (var env.[(evaluatedV.ToString())]) 
                             else env.Add((evaluatedV.ToString()), (evaluatedV.ToString()) + sndKey)
                                  var ((evaluatedV.ToString()) + sndKey)
| LessThen(e1, e2) -> let evaluatedV = (eval  e1).ToString()
                      let evaluatedE = (eval  e2).ToString()
                      let sndKey = "LessThen" + evaluatedE
                      if env.ContainsKey (evaluatedV.ToString()) then 
                            if env.[(evaluatedV.ToString())].Equals(sndKey) then
                                   var env.[(evaluatedV.ToString())]
                            else ~~~ (var env.[(evaluatedV.ToString())]) 
                      else env.Add((evaluatedV.ToString()), (evaluatedV.ToString()) + sndKey)
                           var ((evaluatedV.ToString()) + sndKey)
| Length(e) -> var ("Len" + (eval e).ToString())
| Full(e) -> var ("Full" + (eval e).ToString())
| Minus(e1, e2) -> var ((eval e1).ToString() + (eval e2).ToString())
| Times(e1, e2) -> var ((eval e1).ToString() + (eval e2).ToString())
| Plus (e1, e2) -> var ((eval e1).ToString() + (eval e2).ToString())
| Duration(e) -> eval e
| Minutes(e) -> eval e
| Trim(e) -> var ("Tri" + (eval e).ToString())
| Reverse(e) -> var ("Rev" + (eval e).ToString())
| Ast.And (v1, v2) -> eval v1 &&& eval v2
| Ast.Or (v1, v2) -> eval v1 ||| eval v2
| Ast.Not(v1) -> ~~~ (eval v1)
| _ as a-> failwithf "Expression %A not found" a

编辑:此评估程序用于验证布尔条件。在这个版本中只有一个字典,它的性能更高,但我如何模拟以下情况:

  1. var1 ==“foo1”&amp;&amp; var1 ==“foo2”---&gt;可满足的:假的
  2. var2&gt; = 0&amp;&amp; var2&gt; 0 ---&gt;可满足的:真实的
  3. (var3 ==“foo2”&amp;&amp; var3!= null)|| var3 ==“foo1” - &gt;可满足的:真实的

2 个答案:

答案 0 :(得分:1)

答案是:这取决于。

在某些情况下,最好在你去的时候天真地走树。当你的表达式很小并且只评估一次或只评估几次时,这可能是最好的策略。

如果你的表情很大并且可能会被评估,也许最好尝试做一些优化。任何优化都会产生需要分摊的相关成本,因此这就是为什么最好定位需要多次执行的大表达式。您可以尝试多种类型的优化,这里有一些建议(一个好的编译器教科书无疑会有更多更好的建议):

  • 走你的树寻找案件 这可以提前执行。 例如,如果有两个常量 相邻的你可能会执行 操作(即添加它们 一起)并产生一个新的小 更简单的树。同样,如果你找到 任何乘以零的东西 常数,那么你可以替换它 只需零,就像任何东西一样 乘以零是零。
  • 你可以走路寻找树木 完全相同的分支, 如果这个分支没有副作用, 您可以缓存其结果 并且只执行一次(这可以 甚至可以在不同之间完成 表达式,如有必要)。
  • 您可能想要查看编译 你的数据结构。在.NET中你可以 使用Relection.Emit命名空间,或 通过CodeDom生成代码 生成等效的代码 您的数据结构。这将 加快执行速度,但是 编译将有一个非常 成本高,所以这是一个策略 小心使用。

您实施的任何优化都应该根据'naïve'基线进行仔细测量,在某些情况下,您的优化可能不会按预期运行,并可能导致执行速度变慢。

其他任何非常简单的优化都可能非常难以实现,祝你好运!

答案 1 :(得分:0)

对您的代码进行最简单的优化可能会产生显着的收益,可能会替换您使用(可怕的)F#扩展方法来搜索Dictionary并使用未分配的.NET替代方法。所以这个:

match env.TryGetValue evaluatedV with
| true, v1 ->
    match v1.TryGetValue sndKey with
    | true, v2 -> v2
    | _ ->
        v1.[sndKey] <- evaluetedV + sndKey
        env.[evaluatedV] <- v1
        evaluatedV + sndKey
| _ ->
    if value.Count <> 0 then value.Clear()
    value.[sndKey] <- evaluetedV + sndKey
    env.[evaluatedV] <- value
    evaluatedV + sndKey

变为:

let mutable v1 = Unchecked.defaultof<_>
if env.TryGetValue(evaluatedV, &v1) then
  let mutable v2 = Unchecked.defaultof<_>
  if v1.TryGetValue(sndKey, &v2) then v2 else
    v1.[sndKey] <- evaluetedV + sndKey
    env.[evaluatedV] <- v1
    evaluatedV + sndKey
else
  if value.Count <> 0 then value.Clear()
  value.[sndKey] <- evaluetedV + sndKey
  env.[evaluatedV] <- value
  evaluatedV + sndKey

但是,这些哈希表查找可能会破坏您的性能。有各种各样的技术可以避免这些问题,但它们不是F#特定的:

  • 将两个哈希表查找合并为一个哈希表查找。
  • 用更有效的类型替换字符串,例如散列符号。
  • 使用每个符号中的可变引用替换外部哈希表,从而在valueenv词典中查找该符号的结果。