Skip to content

Commit a2c9eb3

Browse files
committed
fixed 24B/Op memory issue in 1.0.0, will push 1.0.1
1 parent 8b17496 commit a2c9eb3

4 files changed

Lines changed: 60 additions & 23 deletions

File tree

LATEST.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# ParserNG
22

3+
ParserNG 1.0.0 has been released on maven-central!
4+
The library has finally come of age with the introduction of its Turbo mode, which offers a massive speed boost over its normal mode.
5+
The nomal mode already beats famous libraries like exp4J, and rivals Janino, the widely acclaimed Gold Standard of Java math parser speed measurements, very closely
6+
37

48
ParserNG 0.2.5 has been released on maven-central
59

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# ParserNG 🧮⚡
22

3-
**ParserNG 1.0.0** is a **blazing-fast**, **pure Java**, **zero-native-dependencies** math expression parser and evaluator.
3+
**ParserNG 1.0.1** is a **blazing-fast**, **pure Java**, **zero-native-dependencies** math expression parser and evaluator.
44

5-
It **beats Janino, exp4J, and JavaMEP on evaluation speed** across every kind of expression — from simple algebra to heavy trig, matrices, and calculus.
5+
It **beats Janino, exp4J, and com.expression.parser on evaluation speed** across every kind of expression — from simple algebra to heavy trig, matrices, and calculus.
66
With the new **Turbo compiled mode**, it routinely reaches **3–10 million evaluations per second**.
77

88
It goes far beyond basic parsing — offering **symbolic differentiation**, **resilient numerical integration**, **full matrix algebra**, **statistics**, **equation solving**, **user-defined functions**, **2D graphing**, and more — all in one lightweight, cross-platform library.

pom.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<modelVersion>4.0.0</modelVersion>
44
<groupId>com.github.gbenroscience</groupId>
55
<artifactId>parser-ng</artifactId>
6-
<version>1.0.0</version>
6+
<version>1.0.1</version>
77
<packaging>jar</packaging>
88
<!--
99
I started this project 2009 and have been upgrading it since then.
@@ -13,6 +13,8 @@
1313

1414
<name>ParserNG</name>
1515
<description>Rich and Performant, Cross Platform Java Library(100% Java)...Version 1.0.0 explodes even higher in execution speed.
16+
Version 1.0.1 retains the wild speeds of Version 1.0.0 and brings the memory usage of the turbo mode down to 0.001 B/op(same as for the normal mode)
17+
but at nanosecond speeds.Matrix Algebra and other rich features have been optimized in Turbo mode also.
1618
</description>
1719
<url>https://github.com/gbenroscience/ParserNG</url>
1820

src/main/java/com/github/gbenroscience/parser/turbo/tools/ScalarTurboEvaluator.java

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ public class ScalarTurboEvaluator implements TurboExpressionEvaluator {
7272
MethodType vectorType = MethodType.methodType(double[].class, String.class, double[].class);
7373
MethodType vector2Type = MethodType.methodType(double[].class, String.class, String.class);
7474

75-
// Find the static methods
7675
/**
7776
* For non stats methods that return a double array
7877
*/
@@ -193,23 +192,52 @@ public static double invokeRegistryMethod(int methodId, double[] argsValues) {
193192
*
194193
* @return A FastCompositeExpression that returns wrapped scalar
195194
* @throws Throwable if compilation fails
195+
*
196+
* @Override public FastCompositeExpression compile() throws Throwable { //
197+
* This now yields a handle with signature (double[])Object MethodHandle
198+
* scalarHandle = compileScalar(postfix);
199+
*
200+
* return new FastCompositeExpression() {
201+
* @Override public MathExpression.EvalResult apply(double[] variables) {
202+
* try { // invoke() now returns Double (boxed) or double[] Object result =
203+
* scalarHandle.invoke(variables);
204+
*
205+
* if (result instanceof double[]) { return new
206+
* MathExpression.EvalResult().wrap((double[]) result); } return new
207+
* MathExpression.EvalResult().wrap(((Number) result).doubleValue()); }
208+
* catch (Throwable t) { throw new RuntimeException("Turbo evaluation
209+
* failed", t); } }
210+
*
211+
* @Override public double applyScalar(double[] variables) { try { Object
212+
* result = scalarHandle.invoke(variables);
213+
*
214+
* if (result instanceof Number) { return ((Number) result).doubleValue(); }
215+
* // Coercion: If the user calls a vector function in a scalar context, //
216+
* we return the first element to prevent a crash. double[] arr = (double[])
217+
* result; return (arr != null && arr.length > 0) ? arr[0] : Double.NaN; }
218+
* catch (Throwable t) { throw new RuntimeException("Turbo primitive
219+
* execution failed", t); } } }; }
196220
*/
197221
@Override
198222
public FastCompositeExpression compile() throws Throwable {
199-
// This now yields a handle with signature (double[])Object
200-
MethodHandle scalarHandle = compileScalar(postfix);
223+
// The base handle as defined by your compiler (returns Object)
224+
MethodHandle genericHandle = compileScalar(postfix);
225+
226+
// Specialized handle for scalar path: (double[])double
227+
// This forces the JIT to see the primitive return path
228+
MethodHandle scalarPrimitiveHandle = genericHandle.asType(
229+
MethodType.methodType(double.class, double[].class));
201230

202231
return new FastCompositeExpression() {
203232
@Override
204233
public MathExpression.EvalResult apply(double[] variables) {
205234
try {
206-
// invoke() now returns Double (boxed) or double[]
207-
Object result = scalarHandle.invoke(variables);
208-
235+
// Keep Object for the general/vector path
236+
Object result = genericHandle.invokeExact(variables);
209237
if (result instanceof double[]) {
210238
return new MathExpression.EvalResult().wrap((double[]) result);
211239
}
212-
return new MathExpression.EvalResult().wrap(((Number) result).doubleValue());
240+
return new MathExpression.EvalResult().wrap((double) result);
213241
} catch (Throwable t) {
214242
throw new RuntimeException("Turbo evaluation failed", t);
215243
}
@@ -218,15 +246,18 @@ public MathExpression.EvalResult apply(double[] variables) {
218246
@Override
219247
public double applyScalar(double[] variables) {
220248
try {
221-
Object result = scalarHandle.invoke(variables);
222-
223-
if (result instanceof Number) {
224-
return ((Number) result).doubleValue();
249+
// Use invokeExact on the specialized primitive handle
250+
// This will result in ZERO allocations
251+
return (double) scalarPrimitiveHandle.invokeExact(variables);
252+
} catch (ClassCastException cce) {
253+
// Fallback for cases where the result is actually a double[]
254+
// but called in a scalar context
255+
try {
256+
double[] arr = (double[]) genericHandle.invokeExact(variables);
257+
return (arr != null && arr.length > 0) ? arr[0] : Double.NaN;
258+
} catch (Throwable t) {
259+
throw new RuntimeException(t);
225260
}
226-
// Coercion: If the user calls a vector function in a scalar context,
227-
// we return the first element to prevent a crash.
228-
double[] arr = (double[]) result;
229-
return (arr != null && arr.length > 0) ? arr[0] : Double.NaN;
230261
} catch (Throwable t) {
231262
throw new RuntimeException("Turbo primitive execution failed", t);
232263
}
@@ -331,7 +362,6 @@ private static MethodHandle compileScalar(MathExpression.Token[] postfix) throws
331362
String[] rawArgs = t.getRawArgs();
332363
stack.pop();
333364

334-
335365
MethodHandle finalOp = MethodHandles.insertArguments(VECTOR_2_GATEKEEPER_HANDLE, 0, name, rawArgs[0]);
336366

337367
// CRITICAL: You must change the return type to Object.class.
@@ -977,18 +1007,19 @@ private static double[] executeVectorReturningStatsMethod(MethodHandle handle, d
9771007
/**
9781008
* Execution bridge for the TurboRootFinder. This is invoked by the compiled
9791009
* MethodHandle chain.
1010+
*
9801011
* @param baseHandle
9811012
* @param derivHandle
9821013
* @param xSlot
9831014
* @param iterations
9841015
* @param lower
9851016
* @param upper
986-
* @return
1017+
* @return
9871018
*/
9881019
public static double executeTurboRoot(MethodHandle baseHandle, MethodHandle derivHandle,
9891020
int xSlot, double lower, double upper, int iterations) {
9901021
// We use a default iteration cap of 1000 for the turbo version
991-
TurboRootFinder trf = new TurboRootFinder(baseHandle, derivHandle, xSlot, lower, upper, iterations);
1022+
TurboRootFinder trf = new TurboRootFinder(baseHandle, derivHandle, xSlot, lower, upper, iterations);
9921023
return trf.findRoots();
9931024
}
9941025

@@ -1006,8 +1037,8 @@ public static double[] executeQuadraticRoot(String funcHandle) {
10061037
QuadraticSolver alg = solver.getAlgorithm();
10071038
if (alg.isComplex()) {
10081039
return alg.solutions;
1009-
} else {
1010-
return new double[]{alg.solutions[0],alg.solutions[2]};
1040+
} else {
1041+
return new double[]{alg.solutions[0], alg.solutions[2]};
10111042
}
10121043
}
10131044

0 commit comments

Comments
 (0)