Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stored the ram classes before sending the compilationReq to server #9955

Merged
merged 1 commit into from
Jul 11, 2020

Conversation

EmanElsaban
Copy link
Contributor

Stored the romClasses in an unordered_map before sending the compilation
request to the server. I also check if the romClass is in the map, if it is
I send an empty string to the server instead of the romClass again and on the server
side I check to see if I received an empty string. If so I don't cache it.
issue: #9708

Signed-off-by: Eman Elsabban eman.elsaban1@gmail.com

@EmanElsaban
Copy link
Contributor Author

@mpirvu Can you please review my PR? Thank you.

@mpirvu mpirvu added the comp:jitserver Artifacts related to JIT-as-a-Service project label Jun 19, 2020
@mpirvu mpirvu added this to In progress in JIT as a Service via automation Jun 19, 2020
@mpirvu mpirvu self-assigned this Jun 19, 2020
Copy link
Contributor

@mpirvu mpirvu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some inline comments

@@ -1227,6 +1229,7 @@ class CompilationInfo

#if defined(J9VM_OPT_JITSERVER)
ClientSessionHT *_clientSessionHT; // JITServer hashtable that holds session information about JITClients
PersistentUnorderedMap<J9ROMClass*, JITServerHelpers::ClassInfoTuple> _romJ9ClassMap;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need ClassInfoTuple? Maybe all we need here is an unordered_set instead of an unordered_map

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add some comments with the purpose of this hashtable.

@@ -3146,6 +3146,22 @@ remoteCompile(
if (compiler->isOptServer())
compiler->setOption(TR_Server);
auto classInfoTuple = JITServerHelpers::packRemoteROMClassInfo(clazz, compiler->fej9vm()->vmThread(), compiler->trMemory());

// check if the romClass is in the map, if it is not insert in the map
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer multi-line comments to be capitalized --> Check

{
// send an empty string instead of the rom class
// send_compile_request_without_romclass
std::get<0>(classInfoTuple) = " ";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can optimize this path because we seem to serialize the ROMClass information and then throw it away

@@ -528,8 +528,13 @@ TR::CompilationInfoPerThreadRemote::processEntry(TR_MethodToBeCompiled &entry, J
J9ROMClass *romClass = NULL;
if (!(romClass = JITServerHelpers::getRemoteROMClassIfCached(clientSession, clazz)))
{
romClass = JITServerHelpers::romClassFromString(std::get<0>(classInfoTuple), compInfo->persistentMemory());
JITServerHelpers::cacheRemoteROMClass(getClientData(), clazz, romClass, &classInfoTuple);
//check whether the first argument of the classInfoTuple is an empty string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The the ROMClass is not already cached at the server we get the information from the request sent by the client and cache it. If the client did not send us anything, the server needs to specifically ask about the information, because it cannot continue with the ROMClas.

@EmanElsaban
Copy link
Contributor Author

@dmitry-ten

Copy link
Contributor

@mpirvu mpirvu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a few observations. Also I need to think what should happen in the presence of class unloading.

@@ -44,7 +44,7 @@
#include "vmaccess.h"

extern TR::Monitor *assumptionTableMutex;

bool serializeClass = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This global can create problems with multithreading.
Please define a local as close as possible to the usage.

@@ -1171,7 +1171,8 @@ handleServerMessage(JITServer::ClientStream *client, TR_J9VM *fe, JITServer::Mes
case MessageType::ResolvedMethod_getRemoteROMClassAndMethods:
{
J9Class *clazz = std::get<0>(client->getRecvData<J9Class *>());
client->write(response, JITServerHelpers::packRemoteROMClassInfo(clazz, fe->vmThread(), trMemory));
serializeClass = true;
client->write(response, JITServerHelpers::packRemoteROMClassInfo(clazz, fe->vmThread(), trMemory, serializeClass));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you don't need serializeClass; instead you can pass directly true

@@ -298,6 +298,7 @@ TR::CompilationInfoPerThreadRemote::processEntry(TR_MethodToBeCompiled &entry, J
bool abortCompilation = false;
uint64_t clientId = 0;
TR::CompilationInfo *compInfo = getCompilationInfo();
auto comp = getCompilation();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comp object does not exist at this point. Most likely comp == NULL at this point

// It could be a renewed connection, so that's a new server because old one was shutdown
// When the server receives an empty ROM class it would check if it actually has this class cached,
// And if it it's not cached, send a request to the client
romClass = JITServerHelpers::getRemoteROMClass(clazz, stream, comp->trMemory(), &classInfoTuple);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, because comp is likely NULL you cannot use comp->trMemory(). However, looking at the definition of getRemoteROMClass() the only reason it needs trMemory is for trMemory->trPersistentMemory() which you can obtain from compInfo->persistentMemory() as used above. So you need to modify getRemoteROMClass() to accept persistentMemory as a parameter.

JITServerHelpers::cacheRemoteROMClass(getClientData(), clazz, romClass, &classInfoTuple);
// Check whether the first argument of the classInfoTuple is an empty string
// If it's an empty string then I dont't need to cache it
if(std::get<0>(classInfoTuple) != " ")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not empty string, but rather a string with one character. You can actually create an empty string by calling the default constructor and test it with string.empty() function

@@ -324,7 +333,7 @@ JITServerHelpers::packRemoteROMClassInfo(J9Class *clazz, J9VMThread *vmThread, T
uintptr_t classChainOffsetOfIdentifyingLoaderForClazz = fe->sharedCache() ?
fe->sharedCache()->getClassChainOffsetOfIdentifyingLoaderForClazzInSharedCacheNoFail((TR_OpaqueClassBlock *)clazz) : 0;

return std::make_tuple(packROMClass(clazz->romClass, trMemory), methodsOfClass, baseClass, numDims, parentClass,
return std::make_tuple(packROMClass(clazz->romClass, trMemory, serializeClass), methodsOfClass, baseClass, numDims, parentClass,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to pass serializeClass to packROMClass. Instead you could do:
return std::make_tuple(serializeClass ? packROMClass(clazz->romClass, trMemory) : std::string(), methodsOfClass, baseClass, numDims, parentClass,

@dmitry-ten
Copy link
Contributor

I have a concern that's similar to class unloading.
The current implementation will work correctly if the server goes down and another server comes up, i.e. server will detect that it doesn't have a ROM class cached and will send a message to the client.
However, this will result in some extra messages, since the new server doesn't have any ROM classes cached, so pretty much every compilation will result in 1 extra message. I'm not sure if there is an easy solution here. Server would basically need to inform the client that it doesn't have anything cached so that the client can clear its set.

@mpirvu I think we could just look at the list of unloaded classes at the client and delete them from the _romJ9ClassSet. We would need to find the corresponding ROM class from J9Class but that is easy to do.

@mpirvu
Copy link
Contributor

mpirvu commented Jun 24, 2020

I thought about deleting the RomClass from the local set at the client on class unloading. I guess I was trying to over-optimize because one ROMClass can be used by several RAMClasses (with different classloaders) and even if a RAMClass may die the ROMClass can still exist.
I now realized that the cache at the server is based on RAMClasses. Because the client keeps its cache based on ROMClasses it may not send the ROMClass for a second RAMClass that stems from the same ROMClass. This will result in message from the server to the client.
So, I propose that the unordered_set added at the client be based on RAMClasses. This set needs to be purged when a RAMClass gets unloaded.

@mpirvu
Copy link
Contributor

mpirvu commented Jun 29, 2020

In remoteCompile() the j9class for the method to be compiled is readily available: clazz. This is a better key for our set than the ROMClass.
That set needs to be purged when a class gets unloaded. When a class gets unloaded, static void jitHookClassUnload(J9HookInterface * * hookInterface, UDATA eventNum, void * eventData, void * userData) is called. In there we already have code JITServer code for adding to the unloadedClassesTemList:

#if defined(J9VM_OPT_JITSERVER)
   // Add to JITServer unload list
   if (compInfo->getPersistentInfo()->getRemoteCompilationMode() == JITServer::CLIENT)
      compInfo->getUnloadedClassesTempList()->push_back(clazz);
#endif

We just need to augment the code with some lines that would delete the corresponding j9class from our set. Since class unloading is done in a stop-the-world fashion (only one thread is active), acquiring a monitor is not needed.

@EmanElsaban EmanElsaban force-pushed the romClassCache branch 2 times, most recently from 7c09331 to cb6c0f4 Compare June 30, 2020 16:58
@EmanElsaban EmanElsaban changed the title Stored the RomClasses before sending the compilationReq to server Stored the ram classes before sending the compilationReq to server Jun 30, 2020
Copy link
Contributor

@mpirvu mpirvu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some inline comments. Also, you need to increment the minor version for the CommunicationStream because we changed the content of the messages.

@@ -1009,6 +1010,7 @@ class CompilationInfo
}
void setNewlyExtendedClasses(PersistentUnorderedMap<TR_OpaqueClassBlock*, uint8_t> *it) { _newlyExtendedClasses = it; }

TR::Monitor *getRamSetMonitor() const { return _ramSetMonitor; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name RamSet is not very descriptive.
I am not feeling very inspired, but maybe something along the lines classesCachedAtServerMonitor better depicts what this monitor is used for

@@ -2115,6 +2115,12 @@ static void jitHookClassUnload(J9HookInterface * * hookInterface, UDATA eventNum
// Add to JITServer unload list
if (compInfo->getPersistentInfo()->getRemoteCompilationMode() == JITServer::CLIENT)
compInfo->getUnloadedClassesTempList()->push_back(clazz);
// Loop through the set to find the class that needs to be purged once found erase from the set
auto it = compInfo->getRamJ9ClassSet().find(unloadedEvent->clazz);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code should be put under the if that determines we are running on the client.

auto it = compInfo->getRamJ9ClassSet().find(unloadedEvent->clazz);
if (it != compInfo->getRamJ9ClassSet().end())
{
compInfo->getRamJ9ClassSet().erase(it);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can try to erase it even if it does not exist, thus avoiding one search operation when desired item exists in the collection.

OMR::CriticalSection romClassCache(compInfo->getRamSetMonitor());
if (compInfo->getRamJ9ClassSet().find(clazz) == compInfo->getRamJ9ClassSet().end())
{
// clazz was not found send the romclass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is composed from two sentences. Let make that obvious: "clazz not found. Send the romClass to JITServer."

{
// Send an empty string instead of the rom class, first don't serialize the ROMClass
// Send_compile_request_without_romclass
serializeClass = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The else part is not needed, because serializeClass starts as false

// When the server receives an empty ROM class it would check if it actually has this class cached,
// And if it it's not cached, send a request to the client
romClass = JITServerHelpers::getRemoteROMClass(clazz, stream, compInfo->persistentMemory(), &classInfoTuple);
JITServerHelpers::cacheRemoteROMClass(getClientData(), clazz, romClass, &classInfoTuple);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line JITServerHelpers::cacheRemoteROMClass(getClientData(), clazz, romClass, &classInfoTuple); is common for if and else part so it can be taken out and placed after the if-then-else

@@ -350,6 +350,15 @@ JITServerHelpers::getRemoteROMClass(J9Class *clazz, JITServer::ServerStream *str
return romClassFromString(std::get<0>(*classInfoTuple), trMemory->trPersistentMemory());
}

J9ROMClass *
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the previous version of JITServerHelpers::getRemoteClass() needed anymore? If not, we can delete it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it's used in getAndCacheRemoteROMClass() in CompilationThread.cpp and getAndCacheRemoteROMClass is used in other several files. That's why I overloaded the function. For example, here _romClass = threadCompInfo->getAndCacheRemoteROMClass(_ramClass, trMemory); which is in j9methodServer.cpp.

@@ -1227,6 +1230,8 @@ class CompilationInfo

#if defined(J9VM_OPT_JITSERVER)
ClientSessionHT *_clientSessionHT; // JITServer hashtable that holds session information about JITClients
PersistentUnorderedSet<J9Class*> _ramJ9ClassSet;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment here about the _ramJ9ClassSet. Maybe replace with _classesCachedAtServer

@@ -2115,6 +2115,12 @@ static void jitHookClassUnload(J9HookInterface * * hookInterface, UDATA eventNum
// Add to JITServer unload list
if (compInfo->getPersistentInfo()->getRemoteCompilationMode() == JITServer::CLIENT)
compInfo->getUnloadedClassesTempList()->push_back(clazz);
// Loop through the set to find the class that needs to be purged once found erase from the set
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment seems to be composed of two sentences, so let's make that clear:

// Loop through the set to find the class that needs to be purged.
// Once found erase from the set.

@@ -3145,7 +3144,28 @@ remoteCompile(

if (compiler->isOptServer())
compiler->setOption(TR_Server);
auto classInfoTuple = JITServerHelpers::packRemoteROMClassInfo(clazz, compiler->fej9vm()->vmThread(), compiler->trMemory());

// Check if the Clazz is in the set, if it is not insert in the set
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Transform this comment into:

Check the _classesCachedAtServer set to determine whether JITServer is likely to have this class already cached.
If so, do not send the ROMClass content to save network traffic."

Copy link
Contributor

@mpirvu mpirvu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mpirvu
Copy link
Contributor

mpirvu commented Jul 6, 2020

jenkins test sanity plinuxjit,xlinuxjit jdk11

@mpirvu
Copy link
Contributor

mpirvu commented Jul 6, 2020

It just occurred to me that in cases where the server clears its internal caches because of a seqNo that is lost we must destroy the set of cached j9classes at the client as well.
The easiest way is to accomplish this is to clear it when the client receives JITServer::MessageType::getUnloadedClassRangesAndCHTable

@@ -215,6 +215,7 @@ handleServerMessage(JITServer::ClientStream *client, TR_J9VM *fe, JITServer::Mes
auto table = (JITClientPersistentCHTable*)comp->getPersistentInfo()->getPersistentCHTable();
std::string encoded = FlatPersistentClassInfo::serializeHierarchy(table);

compInfo->getclassesCachedAtServer().clear();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This require a mutex.
Maybe it's better to put is after the write operation so that we don't delay it.

@mpirvu
Copy link
Contributor

mpirvu commented Jul 7, 2020

Also, you need to increment the minor version for the CommunicationStream because we changed the content of the messages.

I realized that this is missing, so please increment the minor version.

Stored the ramClasses in an unordered_set before sending the compilation
request to the server. I check if the ram class (clazz) is cached in the set.
if it is not stored then it will be cached in the set and will send the corresponding
ROM by setting the serializeClass bool to true. If the clazz is already in the cache set
this means it has already been sent to the server. So send the clazz again but with empty ROM
class (empty string for ROM Class) by setting serializeClass bool to false.
issue: eclipse-openj9#9708

Signed-off-by: Eman Elsabban <eman.elsaban1@gmail.com>
Copy link
Contributor

@mpirvu mpirvu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mpirvu
Copy link
Contributor

mpirvu commented Jul 9, 2020

jenkins test sanity plinuxjit,xlinuxjit jdk11

@mpirvu mpirvu merged commit a105721 into eclipse-openj9:master Jul 11, 2020
JIT as a Service automation moved this from In progress to Done Jul 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
comp:jitserver Artifacts related to JIT-as-a-Service project
Projects
Development

Successfully merging this pull request may close these issues.

None yet

3 participants