Before getting into the results, a couple of general words about the state of (open source) expression evaluator libraries in Java (I couldn’t test closed source ones because they impose restrictions – like “only 15 expressions evaluated” – which make it impossible to benchmark them):

None of them are in Maven central (or even in Maven for that matter – with the exception of Janino, but there you still need a different repo to get the latest version)

Their development is mostly stalled

They are lacking in features and/or are buggy

The benchmark consist in evaluating the 2 + (7-5) * 3.14159 * x + sin(0) expression (similar to the original article). Unfortunately the exponentiation part had to be eliminated because not all libraries support it. Also, x had to be fixed to 0 because parsii erroneously always considers it zero.

Most of the libraries (JEPLite and MathEval being the exception) separate the idea of “compiling” and “evaluating” the expression. This can be very useful if we wish to evaluate the same expression for a multitude of values of the contained variable. Thus the benchmark tests two cases: compile + evaluate and only evaluate (depending on your usecase one or the other is more relevant). Also, Jeval is by default excluded from the benchmark since it performs so poorly that it eclipses all the other results. If you wish to see just how poorly it performs, uncomment the relevant methods from BenchmarkExpressionEvaluation.

As you can see, the custom implementation beats parsii by a factor of 10x! So how does it work? You can always check out the source code, but I will also explain: It uses Janino to compile a class of the form:

neither of the safety measures efficiently prevent the potential denial of service conditions (like creating infinite loops or consuming all the memory)

also, this is just a proof of concept – for a more complete solution one would need to add more interfaces (like BinaryDoubleFunction, TernaryDoubleFunction and also UnaryLongFunction, etc) as well as some settings related to the naming of the parameters.

Still, this is a great example as to the power of the Java ecosystem and what can be achieved by thinking a little outside of the box.

If you run the benchmark for yourself, you might find a couple of interesting things:

the “Janino” test continuously warns about recompilation. This is expected if you think a little bit about it – since at every invocation it “compiles” the expression. The same is true for JaninoFastexpr

both Janino and JaninoPrecompiled complain about excessive garbage collection. This is because they use auto-boxing (varargs) to pass the parameters which isn’t very efficient (generates a lot of garbage)

This is it folks, enjoy the source code and hopefully this will inspire people to find better solutions to their problems!