Reputation: 91
I'm trying to track down the source of a performance problem with the groovy eval(string) method being called within a Java application. If I execute the following code;
String pattern = "test = ['one','two','three']";
engine.eval(pattern)
it runs in virtually no time (0 to 1ms)
However, if I say the following
String first = "['one','two','three']";
String pattern = "test = " + first; // "identical" String to first approach
engine.eval(pattern)
it takes ~30+ ms to run.
What's worse, after several thousand invocations it will be as high as 60 - 70ms although I'm far less concerned about this than the time delta between the two implementations.
Any explanations as to why this is happening / suggestions on how to avoid this? I suspect it has something to do with the Java and/or Groovy compiler and I've started looking at the compile() method but I'd prefer it if there's a simple way to make the existing code work (less things to change that way).
Upvotes: 2
Views: 2518
Reputation: 6508
If you look at https://github.com/groovy/groovy-core/blob/master/subprojects/groovy-jsr223/src/main/java/org/codehaus/groovy/jsr223/GroovyScriptEngineImpl.java?source=cc you will find a cache for mapping Strings to classes. The problem is that the ManagedConcurrentMap is basically an identity hashmap, meaning if you use the same string over and over again it will be fast, because compilation is skipped for subsequent runs. The problem version creates a new string every time, thus it needs to be compiled every time, causing a new class being generated every time as well.
On how to avoid this: create a hash based cache
Upvotes: 1
Reputation: 91
Ultimately we recognized that
eval("['one','two','three']")
returns exactly that, so
Object test = engine.eval(first)
is equivalent to
engine.eval("test = " + "['one','two','three']") // see text above for exact syntax
Object test = engine.get("test")
and yet it runs in effectively no time at all.
I'm still very curious why making the pattern dynamic has such a dramatic impact on the performance of the eval() but I suspect it will take someone with a deep understanding of the Java and/or groovy runtime elements to shed any light on it.
Thanks to everyone who tried to assist. Regards,
ps.
Upvotes: 0
Reputation:
How are you measuring the time for the invocation? if possible, I would measure each part (the String building and the eval()) to determine which is taking the time, and which increases with the number of iterations. Since you say that the time increases as the number of iterations increases, look at garbage collection. The first case uses a single String - in the later case you are building a new String each iteration, so are consuming memory. You may be running up against a heap limit.
One very useful tool for looking at this is VisualVM. http://visualvm.java.net/
Upvotes: 0
Reputation: 33993
eval
's in any language are notoriously slow and often advised to stay away from ("eval is evil"). However, in your code it seems more that the string concatenation is causing the slowdown, rather than the eval itself.
Still, a few milliseconds shouldn't be cause for concern -- have you determined that this is causing a performance bottleneck in your code? Micromanaging the code may lead to bugs and unclear code in the future which may have much worse implications.
Upvotes: 0