Cache stringConstant results on JITServer to reduce ResolvedMethod_stringConstant messages - #24448
Conversation
51b8812 to
ee0c31e
Compare
mpirvu
left a comment
There was a problem hiding this comment.
The code looks ok but there is room for improvement.
The solution creates a secondary map with the same key <class, cpIndex>. Why not have the same map <key,value> with an extended value?
What is the hit rate of the implemented cache? How many messages were before and after this change?
Depending on the answer we could go one step further: the cache is per compilation, so it gets destroyed when the compilation is done, only to be recreated for the next compilation (if needed). We could have a cache per client (for all compilations stemming from that client) because classes don't become invalid when a compilation ends. The disadvantage of this solution is that the memory for the cache will not be freed at the end of the compilation.
45ef451 to
6d51d91
Compare
In my latest push I combined the two maps into a single |
This seems like a great reduction and based on this alone I would just proceed with this solution. |
I ran the AcmeAirEE8 benchmark with JITServer. The reduction is less dramatic than my simple benchmark but on AcmeAirEE8, Master sent 48 |
Something is off. I remember this being in the thousands. |
I ran AcmeAirEE8 with various configurations. Master still consistently gets around 29-48 |
|
I just had a run of AcmeAirEE8. The number of |
|
Yeah I'm applying load with JMeter using 10 threads for 180 seconds. My total messages are around 2700-2800 and I'm getting about 31 |
|
10 JMeter threads should be enough. Maybe run is too short, especially if you are using a single CPU for Liberty. I use 10 minutes of load. For a cold run (with empty SCC) there are about 40K messages. For warm run (SCC populated by the first run) I get 20K total messages. |
|
I've tried various configurations (10 minute runs, empty SCC, with and without AOT cache) and consistently get my same results as before. All compilations seem to happen during Liberty startup (about 190 compilations), with no new compilations triggered during the 10 minute JMeter load. What Liberty image and configuration are you using for the benchmark? |
That's a red flag right there. There should be thousands of compilations during JMeter load under any configuration. The version of Liberty should not matter that much. I would say that JMeter does not connect at all to Liberty, but I cannot reconcile the reasonable throughput value you see: "2000-4000 req/s". Is JMeter reporting errors? Regardless, could you please push your newest version of the code so that I can have a look? There could be some optimizations at the point of the call. |
|
I found the issue, I followed your repo more closely and tried again, I get about 12K |
mpirvu
left a comment
There was a problem hiding this comment.
While the solution works, it can be optimized further. TR_ResolvedJ9JITServerMethod::isUnresolvedString(I_32 cpIndex, bool optimizeForAOT) does not do any caching which forces TR_ResolvedJ9JITServerMethod::stringConstant() to send another message.
Sometimes isUnresolvedString() is used by itself (without stringConstant()) and therefore no caching will take place. Example:
if (isUnresolved && node->getSymbol()->isConstString()) {
TR::ResolvedMethodSymbol *rms = comp()->getOwningMethodSymbol(node->getOwningMethod());
isUnresolved
= rms->getResolvedMethod()->isUnresolvedString(node->getSymbolReference()->getCPIndex(), true);
}
return isUnresolved;
In other cases we call stringConstant() followed by isUnresolvedString(). In these cases caching works fine:
void *stringConst = owningMethod->stringConstant(cpIndex);
TR::SymbolReference *symRef;
bool isString = true;
if (owningMethod->isUnresolvedString(cpIndex)) {
symRef = findOrCreateCPSymbol(owningMethodSymbol, cpIndex, TR::Address, false, 0);
symRef->setOffset((uintptr_t)stringConst);
} else {
Yet, in other cases we first call isUnresolvedString() and only then stringConstant(). Again, we may send two messages because only stringConstant() caches the result sent by the client:
if (method()->isStringConstant(cpIndex) && !method()->isUnresolvedString(cpIndex)) {
uintptr_t *location = (uintptr_t *)method()->stringConstant(cpIndex);
I think we should also send the ResolvedMethod_stringConstant messages for isUnresolvedString() and cache the result. This way no matter the order of the two frontend calls, the second one will always find the desired data cached.
Furthermore, the calling points of these frontend APIs can be changed so that we have a single cache access. Maybe that can done in a separate PR.
…ringConstant messages
6d51d91 to
7947901
Compare
|
I've updated the branch with the optimizations, but the average number of |
Caches the result of
stringConstant(cpIndex)on the JITServer side to avoid sending repeatedResolvedMethod_stringConstantmessages to the client for the same(class, cpIndex)pair within a compilation.The cache uses
(TR_OpaqueClassBlock *, int32_t)as the key, mirroring the existingisUnresolvedStringcache, sincecpIndexrefers to a constant pool which is associated with a class rather than an individual resolved method.Issue: https://github.ibm.com/runtimes/rt-tr-common-repo/issues/30