一个极其微小的优化想法

在看公司代码的时候发现这么一个东西,就是遍历表达式树,然后把调用Where方法的(MethodCallExpression)表达式下面的条件记录在一个Collection里面。具体细节还没有看的太仔细,但是它大体上是这样干的:

用一个Stack<bool>,记录当前状态,在遍历开始Push(false),然后遍历表达式树的时候,如果该节点是调用Where方法的表达式,则Push(true),其他则Push(false),访问完毕一个表达式以后Pop()。在需要判断的时候用Peek()来判断。

我写了一个大概的模拟代码如下:(如果是直接在Where方法下面的表达式,则放入一个collection)

首先模拟的表达式是一个string数组:

            string[] expressionTree = new string[]
            {
                "A",
                "B",
                "C",
                "D",
                "WHERE",
                "Condition1",
                "Condition1A",
                "Condition1B",
                "Condition1C"
            };
然后是模拟的遍历部分:(index为模拟表达式树的数组的当前索引)
        static List<string> collection;
        static Stack<bool> stack;
        static int index;
        static string[] expressionTree;
 
        static void test1Main(string[] iexpressionTree)
        {
            index = 0;            
            collection = new List<string>();
            stack = new Stack<bool>();
            stack.Push(false);
            expressionTree = iexpressionTree;
 
            test1(expressionTree[index]);
        }
 
 
        static void test1(string expression)
        {
            if (stack.Peek())
                collection.Add(expression);
 
            switch (expression)
            {
                case "WHERE":
                    stack.Push(true);
                    //DOSomeThing
                    if (++index == expressionTree.Length)
                        break;
                    else
                        test1(expressionTree[++index]);
 
                    stack.Pop();
                    break;
                default:
                    stack.Push(false);
                    if (++index == expressionTree.Length)
                        break;
                    else
                        test1(expressionTree[++index]);
                    stack.Pop();
                    break;
            }            
        }
程序最后记录的条件应该是“Condition1”。

那么有没有比这个更加有效率的解决方案呢,暂时想到了2个。

第一个由于这个要处理的表达式中至多有一次Where方法的调用,我可以用一个int来做这个事情,最开始将int设置为1,如果当前遍历的是Where方法的调用节点,则将int设置为0,否则在对应Push的部分把这个int加1,再对应Pop的部分把这个int-1。如果当前该int的值为0则认为当前是在Where方法下面的条件。在只有一个Where的时候,这个方法可以正常运转。

但是这样做显然与原来堆栈的做法分歧较大,可能产生扩展上的不方便。于是又想到了另外一个模拟堆栈的方法,就是用一个uint(姑且称为stackuint),初始设置为0,然后设置一个uint的flag为0x00000001 ,如果当前是Where,则通过stackuint与flag取或把最低位设置为1

stackbyte |= flag

然后对应以前Push(false)的部分把该uint左移一位,对应Pop的部分把该uint右移1位。

在判断的时候用

(stackuint&flag) != 0
如果不为0,则认为当前语境是在Where条件下。这个方法与Stack的方法基本上是等效的,只是要求遍历
表达式的深度不能超过UInt的位数。
而这个条件应该是可以满足的(刨除某些变态的情况)。
最后附上三种方法执行1000万次所花费时间的测试结果:

posted on 2011-04-07 12:03  老哈  阅读(335)  评论(0编辑  收藏  举报

导航