一个极其微小的优化想法
时间:2011-04-07 来源:老哈
在看公司代码的时候发现这么一个东西,就是遍历表达式,然后把调用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万次所花费时间的测试结果: