Optimize getMethodFromName calls in J9VMServer - #23183
Conversation
|
Note, it will be a while before this is merged, as testing needs to be done to demonstrate an actual performance improvement. |
|
A quick explanation ContextOne of the VM methods that JITServer requires It seems like all calls to this method just reference hard-coded strings. However, from this issue (https://github.ibm.com/runtimes/rt-tr-control-repo/issues/24), it seems like this message is sent hundreds of times. While this is a very small amount in the grand scheme of things
Actual Implementation
The first of these calls is already cached, so we add a caching mechanism for the second
|
mpirvu
left a comment
There was a problem hiding this comment.
Does the code run? You are missing the initialization of the newly created monitor which should cause a crash.
Also, your code always creates a local copy of the new hashtable, thus the hit rate is going to be 0.
I am also concerned about the overhead of scanning the entire hashtable for every class that gets unloaded (O(m*n)) while holding the romMapMonitor. This is probably the most contended monitor at the server.
c53037d to
e26efad
Compare
mpirvu
left a comment
There was a problem hiding this comment.
Apart from the smaller inline suggestions, there are some bigger concerns with the current implementation.
- A name does not uniquely determine a method. You can have a class being loaded by two different classloaders, so the resulting j9class entities will have the same class name (and same method names) but their are considered different otherwise. If you look at the existing code, when we retrieve a class from its name we also specify the classloader (e.g
PersistentUnorderedMap<ClassLoaderStringPair, TR_OpaqueClassBlock*> _classBySignatureMap; - Not all classes are cached by the server. Your code could cache a {name --> j9method} mapping and the code that needs to delete the mapping (because of a class unload) will not be able to find it (because you cannot find the j9class, so you cannot find the name).
- For a large application there could be a very large number of methods cached and the keys (composed of 3 std::strings) could also be large. This may end-up taking too much memory, especially relative to the improvement that this feature is going to provide (TBD).
|
Ignoring the functional issues mentioned above, it would be interesting to get a feel for the hit rate of this proposed cache. If the hit rate is not very high (75+%) the complications and overhead of the implementation may not be worthwhile. |
|
Here's my proposed design (which still needs to be validated from performance point of view): We can delete the JITServer implementation of For deleting from the |
d888a29 to
a4ec773
Compare
a4ec773 to
c064832
Compare
|
This is the performance data for OpenJ9 on AcmeAirEE8 using jitserver without this change Message Stats, per compilation This is the performance data for OpenJ9 on AcmeAirEE8 using jitserver with this change Message Stats, per compilation It seems like there was a substantial improvement, especially to startup time |
When looking at performance you always have to look at the level of fluctuations. The 95% confidence interval for start-up time is 3.8% and 3.1%. This means that, statistically speaking, you cannot distinguish between 2 JVMs that are ~7% apart. You need a large number of runs to reduce that confidence interval. My previous experiments showed about ~730 VM_getMethodFromName messages which are likely to disappear with your change (looking at AcmeAirEE8, warm run). The total number of messages is ~200,000. This means that this PR can cut about ~0.35% of total messages and the improvements (CompCPU especially) should be in the same ballpark. |
c064832 to
c361af4
Compare
|
In comment #23183 (comment) I was suggesting to use the new cache only for system classes. Given how
Purging from cache can be simplified: For the vast majority of the caches the linear scan of the newly created cache ca be avoided because system classes are never unloaded and they are rarely modified. |
77065cb to
2be515b
Compare
mpirvu
left a comment
There was a problem hiding this comment.
In ClientSessionData::processUnloadedClasses(const std::vector<TR_OpaqueClassBlock*> &classes, bool updateUnloadedClasses) we traverse the list of unloaded classes and for each such class we compute the classloader
for (auto clazz : classes)
{
...
J9ClassLoader *cl = (J9ClassLoader *)(it->second._classLoader);
}
At this point you can determine whether the classloader is the system classloader. If it is (unlikely) you should add this class to a locally defined vector.
Later on you should process the elements from this vector by scanning the newly added cache for a matching class. In the vast majority of the cases this new vector is going to be empty because system classes cannot be unloaded and they are rarely redefined.
1b01fa6 to
9b2d05c
Compare
9b2d05c to
ca0bc63
Compare
f2634c7 to
57f7f78
Compare
mpirvu
left a comment
There was a problem hiding this comment.
I have some inline comments.
57f7f78 to
3b66a1a
Compare
mpirvu
left a comment
There was a problem hiding this comment.
LGTM. I only have a small request for an extra comment/explanation.
| { | ||
| ClassMethodNamePair key{(J9Class*)methodClass, std::string(methodName) + signature}; | ||
| PersistentUnorderedMap<ClassMethodNamePair, TR_OpaqueMethodBlock*> &methodMap = _compInfoPT->getClientData()->getMethodByNameMap(); | ||
| if (!callingClass) // only cache methods whose calling class is (nil) |
There was a problem hiding this comment.
It would be better to explain the meaning/purpose of the callingClass. This is explained in the base implementation.
* If callingClass is non-null, a visibility check will be done during the look up.
* Only methods visible to the callingClass will be returned.
The caching mechanism can be simplified if we use it only when callingClass == NULL.
|
The explanation in #23183 (comment) needs an update. |
Replace getMethodFromName on J9VM_Server with the base implementation, and add caching for getMethodFromClass to reduce the number of calls to getMethodFromName
3b66a1a to
17f916d
Compare
|
jenkins test sanity plinuxjit,xlinuxjit,zlinuxjit,alinux64jit jdk21 |
|
Failures: plinux: jdk_concurrent_1, java/util/concurrent/ArrayBlockingQueue/WhiteBox.java, zlinux and zlinux: vector API failures. These are known |
Optimize
fe->getMethodFromName(...)Replace getMethodFromName on J9VMServer with the
base implementation, and add caching for getMethodFromClass
to reduce the number of total messages.