我编写了自己的数学解析器,出于某种原因,当我尝试分析解析器时,需要花费越来越多的时间进行解析。
为了测试我使用了这个输入:Cmd.NUM_9,Cmd.NUM_0,Cmd.NUM_0,Cmd.DIV,Cmd.NUM_2,Cmd.ADD,Cmd.NUM_6,Cmd.MULT,Cmd.NUM_3
单次执行~1.7ms
3000次重复~1,360ms
6000次重复〜5,290ms
9000次重复~11,800ms
剖析器说64%的时间花在了这个功能上: 这是我允许隐式乘法的函数。
private void enableImplicitMultiplication(ArrayList<Cmd> input) {
int input_size = input.size();
if (input_size<2) return;
for (int i=0; i<input_size; i++) {
Cmd cmd = input.get(i);
if (i>0) {
Cmd last = input.get(i-1);
// [EXPR1, EXPR2] => [EXPR1, MULT, EXPR2]
boolean criteria1 = Cmd.exprnCmds.contains(cmd) && Cmd.exprnCmds.contains(last);
// [CBRAC, OBRAC] => [CBRAC, MULT, OBRAC]
// [NUM_X, OBRAC] => [NUM_X, MULT, OBRAC]
boolean criteria2 = cmd==Cmd.OBRAC && (last==Cmd.CBRAC || Cmd.constantCmds.contains(last));
// [CBRAC, NUM_X] => [CBRAC, MULT, NUM_X]
boolean criteria3 = last==Cmd.CBRAC && Cmd.constantCmds.contains(cmd);
if (criteria1 || criteria2 || criteria3) {
input.add(i++, Cmd.MULT);
}
}
}
}
这里发生了什么?
我执行了这样的重复:
public static void main(String[] args) {
Cmd[] iArray = {
Cmd.NUM_9,Cmd.NUM_0,Cmd.NUM_0,Cmd.DIV,Cmd.NUM_2,Cmd.ADD,Cmd.NUM_6,Cmd.MULT,Cmd.NUM_3
};
ArrayList<Cmd> inputArray = new ArrayList<Cmd>(Arrays.asList(iArray));
DirtyExpressionParser parser = null;
int repeat=9000;
double starttime = System.nanoTime();
for (int i=0; i<repeat; i++) {
parser = new DirtyExpressionParser(inputArray);
}
double endtime = System.nanoTime();
System.out.printf("Duration: %.2f ms%n",(endtime-starttime)/1000000);
System.out.println(parser.getResult());
}
构造函数如下所示:
public DirtyExpressionParser(ArrayList<Cmd> inputArray) {
enableImplicitMultiplication(inputArray); //executed once for each repeat
splitOnBrackets(inputArray); //resolves inputArray into Expr objects for each bracket-group
for (Expr expr:exprArray) {
mergeAndSolve(expr);
}
}
答案 0 :(得分:3)
您的微基准测试代码完全错误:JVM上的微基准测试本身就是一种工艺,最好留给jmh或Google Caliper等专用工具。您没有预热代码,不控制GC暂停等等。
通过分析代码得出的一个细节是:
ArrayList
; ArrayList
的重量级操作:必须复制插入元素后列表的全部内容。您应该至少为每次调用创建一个新的ArrayList
,但这不会使您的整个方法正确。
根据我们在评论中的讨论,我诊断出您可能对理解代码时遇到的以下问题:
在Java中,没有一个变量,其值是一个对象。变量的值是对象的引用。因此,当您说new DirtyExpressionParser(inputArray)
时,构造函数不会收到自己的列表私有副本,而是引用到您的ArrayList
main
您已在{ {1}}方法。下一个构造函数调用获取相同的列表,但现在由早期调用修改。这就是为什么你的名单一直在增长的原因。