UPDATE: It looks like my 13.0.1 is calling code from this diffed CharMatcher.
I seem to be having an issue with Guava's Charmatcher. I am currently writing some JUnit tests and I am noticing the first test takes a bit of time (15+ seconds), but the future tests are all approximately the same (.3 seconds), with a majority of the tests running through the same code.
My analysis of the code has pinpointed that the Charmatcher in Guava's library seems to be the culprit. It looks like there is a bit of static initialization code in CharMatcher that might be the true reason it is taking so long. Is there any way I can disable or optimize this behavior? Is there something I am missing from the wiki pages or help files?
Guava version 13.0.1, System: Linux 64, Eclipse 3.6.1
EDIT: I built a test app with the following code:
import com.google.common.base.CharMatcher;
public class Main {
public static void main(String[] args) {
// Using system instead of stopwatch to isolate library.
long startTime = System.currentTimeMillis();
CharMatcher.is(' ');
long endTime = System.currentTimeMillis();
System.out.println("took " + String.valueOf(endTime-startTime) + " ms");
startTime = System.currentTimeMillis();
CharMatcher.is('d');
endTime = System.currentTimeMillis();
System.out.println("2nd took " + String.valueOf(endTime-startTime) + " ms");
}
}
This resulted in the following output:
took 15945 ms
2nd took 0 ms
I ran this in eclipse minus the JUnit framework and just google's guava library. Also I packaged an executable char and received similar results. I'll come back with a second edit after I run a profiler through it.
Thanks for any help.
Edit 2: Results from profiling:
Main.main(String[]) 22,556 ms
com.google.common.base.CharMatcher.<clinit>() 22.556 ms
com.google.common.base.CharMatcher.precomputed() 22,550 ms
com.google.common.base.Platform.precomputeCharMatcher(CharMatcher) 22,550 ms
com.google.common.base.CharMatcher.precomputedInternal() 22,550 ms
com.google.common.base.CharMatcher.slowGetChars() 13,638 ms
com.google.common.base.CharMatcher.setBits(CharMatcher$LookupTable) 8,911 ms
Update: This is fixed in Guava 14.
You are running into some kind of JIT bug present in u21, and likely earlier versions, but not in at least some later JDK versions. Here's the -XX:+PrintCompilation output for your test program in u21:
Here's the same output for u34:
As you can see, it is a hell of a lot more sane in u34. It looks like what is happening in u21 is that some combination of OSR (affecting
slowGetChars
method as it has a large loop), and recursive megamorphic calls (affectingCharMatcher$Or
and other subclasses ofCharMatcher
- which are called at the end of the recursiveCharMatcher$Or
chain) produce a perfect storm of JIT de-optimization and subsequent recompilation, which is resolved in u34.Still, I think the Guava guys are being a bit abusive by creating a 65k element LUT during static init, even if you never use the matchers in question, which takes still about ~50ms when JIT is working properly. I filed a bug against guava, let's see if the maintainers agree :). Update: the bug was fixed within a couple of days of me filing this, the fix will be in Guava 14. That's what I call quick turnaround!
Original post follows:
I have the exact same behavior and ran into this post looking for a solution.
Looking at the code, it seems that they are making a 65k element (each element takes one bit) lookup table and checking if every character matches, which in this requires many nested function calls due to the fluent style being used to define the matchers:
That's the code that is triggering the long static init, which on my fast box takes ~7 seconds. Here's the output of your app on my box:
I'm filing a guava bug.
I just ran the exact benchmark you provided, and got
...Perhaps the problem is on Eclipse's end? Are you running in debug mode?
Just tested with :
and it took 0.06sec to execute. Also looked at the CharMatcher code, and nothing could explain the slow behavior you encounter. You should replace the CharMatcher by a mock to see that it is not the issue and maybe use a profiler to find out the cause.
As a quick help, try to switch to the newest version from the source repo. There are changes there, e.g. the method
slowGetChars
is gone. It used to be a bit slow, but nowhere near what's happening to you.For everybody else it runs some hundred times faster and without being able to reproduce it it's hard to help.