Skip to content

Commit 096d338

Browse files
committed
fixed memory hogging. ParserNG does basically 0 allocations in its high frequency evaluate loop iterations. Beats exp4J widely and Janino very well
1 parent d9e6429 commit 096d338

1 file changed

Lines changed: 22 additions & 13 deletions

File tree

‎src/main/java/com/github/gbenroscience/parser/MathExpression.java‎

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public class MathExpression implements Savable, Solvable {
101101
*/
102102
private boolean willFoldConstants = true;
103103

104-
private ExpressionSolver expressionSolver = new ExpressionSolver();
104+
private ExpressionSolver expressionSolver;
105105

106106
/**
107107
* If set to true, MathExpression objects will automatically initialize
@@ -1356,32 +1356,41 @@ private void compileToPostfix() {
13561356
executionFrame[t.frameIndex] = t.v.getValue();
13571357
}
13581358
}
1359+
expressionSolver = new ExpressionSolver();
13591360
}
13601361

13611362
private final class ExpressionSolver {
13621363

13631364
private static final int MAX_ARITY = 32;
13641365
private static final int ABSOLUTE_MAX_ARITY = 100_000;
13651366

1366-
private EvalResult[][] argCache = new EvalResult[MAX_ARITY + 1][];
1367-
private EvalResult[] stack;
1367+
// Pre-allocate and reuse
1368+
private final EvalResult[] stack;
1369+
private final EvalResult[][] argCache;
13681370

13691371
public ExpressionSolver() {
1370-
// Pre-allocate ONE array per arity that we reuse
1372+
// Allocate stack ONCE, with sufficient capacity
1373+
// Max stack depth is usually cachedPostfix.length, but we pad for safety
1374+
int maxStackDepth = Math.max(cachedPostfix.length * 2, 256);
1375+
this.stack = new EvalResult[maxStackDepth];
1376+
1377+
// CRITICAL: Initialize all stack slots with EvalResult objects
1378+
for (int i = 0; i < stack.length; i++) {
1379+
stack[i] = new EvalResult();
1380+
}
1381+
1382+
// Pre-allocate argument arrays
1383+
this.argCache = new EvalResult[MAX_ARITY + 1][];
13711384
for (int i = 0; i <= MAX_ARITY; i++) {
13721385
argCache[i] = new EvalResult[i];
1386+
for (int j = 0; j < i; j++) {
1387+
argCache[i][j] = new EvalResult(); // Initialize
1388+
}
13731389
}
13741390
}
13751391

13761392
public EvalResult evaluate() {
1377-
if(stack == null){
1378-
stack = new EvalResult[Math.max(cachedPostfix.length * 2, 64)];
1379-
}else{
1380-
if(cachedPostfix.length > stack.length ){
1381-
stack = new EvalResult[Math.max(cachedPostfix.length * 2, 64)];
1382-
}
1383-
}
1384-
1393+
// Just use the pre-allocated stack - no allocation per call
13851394
int ptr = -1;
13861395

13871396
for (int i = 0; i < cachedPostfix.length; i++) {
@@ -1452,7 +1461,7 @@ public EvalResult evaluate() {
14521461
int valuesOnStack = ptr + 1;
14531462

14541463
if (arity == 0) {
1455-
EvalResult result = t.action.calc(getNextResult(), arity,argCache[arity]);
1464+
EvalResult result = t.action.calc(getNextResult(), arity, argCache[arity]);
14561465
stack[++ptr] = result;
14571466
break;
14581467
}

0 commit comments

Comments
 (0)