Skip to content

Commit 1981e13

Browse files
committed
Implement KnownObjectTable caching
This commit is mainly for the benefit of JITServer. Experiments have shows that addKnownObjectConstraints() function used by GlobalVP generates many server-client messages when calling: fej9()->getObjectClassInfoFromObjectReferenceLocation() to fetch some class information about a particular known object. This commit implements a caching mechanism for that class information as described below: An entry in the knownObjectTable is extended to include the following fields: uintptr_t *_jniReference; // This existed before this commit TR_OpaqueClassBlock *_clazz; TR_OpaqueClassBlock *_jlClass; bool _isFixedJavaLangClass; bool _isString; When a new entry is added into the knownObjectTable, only the "old" field _jniReference will be populated; the others will be set to default values, NULL or false. If GlobalVP is asking about the other fields and they are not yet populated, the server will send a message, find the value of these other fields and store them into the knownObjectTable. If these fields are already populated when GlobalVP asks about them, they will be immediately retrieved from the knownObjectTable entry, saving a message. Note that the same caching mechanism is going to be used at the client, but the benefit is expected to be low. Experimental results have shown a 84% reduction of the messages sent by the server during addKnownObjectConstraints(). There are two options that control this feature: -Xjit:disableKnownObjectTableCaching disables the caching mechanism and always asks the client for the desired information. -Xjit:enableKnownObjectTableCachingVerification when caching is enabled this option always sends a message to the client, fetches the desired information and then compares it against the cached information. If the two don't match, a fatal assert is triggered. Depends on: eclipse-omr/omr#8056 Signed-off-by: Marius Pirvu <[email protected]>
1 parent c05527b commit 1981e13

File tree

11 files changed

+175
-114
lines changed

11 files changed

+175
-114
lines changed

runtime/compiler/compile/J9Compilation.cpp

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -851,22 +851,9 @@ J9::Compilation::freeKnownObjectTable()
851851
{
852852
if (_knownObjectTable)
853853
{
854-
#if defined(J9VM_OPT_JITSERVER)
855-
if (!isOutOfProcessCompilation())
856-
#endif /* defined(J9VM_OPT_JITSERVER) */
857-
{
858-
TR::VMAccessCriticalSection freeKnownObjectTable(self()->fej9());
859-
860-
J9VMThread *thread = self()->fej9()->vmThread();
861-
TR_ASSERT(thread, "assertion failure");
862-
863-
TR_ArrayIterator<uintptr_t> i(&_knownObjectTable->_references);
864-
for (uintptr_t *ref = i.getFirst(); !i.pastEnd(); ref = i.getNext())
865-
thread->javaVM->internalVMFunctions->j9jni_deleteLocalRef((JNIEnv*)thread, (jobject)ref);
866-
}
854+
_knownObjectTable->freeKnownObjectTable();
855+
_knownObjectTable = NULL;
867856
}
868-
869-
_knownObjectTable = NULL;
870857
}
871858

872859

runtime/compiler/control/JITClientCompilationThread.cpp

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,14 @@ handleServerMessage(JITServer::ClientStream *client, TR_J9VM *fe, JITServer::Mes
673673
knot->getPointerLocation(ci.knownObjectIndex));
674674
}
675675
break;
676+
case MessageType::VM_getObjectClassInfoFromKnotIndex:
677+
{
678+
auto recv = client->getRecvData<TR::KnownObjectTable::Index>();
679+
TR::KnownObjectTable::Index knotIndex = std::get<0>(recv);
680+
TR::KnownObjectTable::ObjectInfo objInfo = fe->getObjClassInfoFromKnotIndex(comp, knotIndex);
681+
client->write(response, objInfo);
682+
}
683+
break;
676684
case MessageType::VM_stackWalkerMaySkipFrames:
677685
{
678686
client->getRecvData<JITServer::Void>();
@@ -2781,41 +2789,13 @@ handleServerMessage(JITServer::ClientStream *client, TR_J9VM *fe, JITServer::Mes
27812789
std::string((char*) bodyInfo->getMethodInfo(), sizeof(TR_PersistentMethodInfo)));
27822790
}
27832791
break;
2784-
case MessageType::KnownObjectTable_getOrCreateIndex:
2785-
{
2786-
uintptr_t objectPointer = std::get<0>(client->getRecvData<uintptr_t>());
2787-
TR::KnownObjectTable::Index index = TR::KnownObjectTable::UNKNOWN;
2788-
uintptr_t *objectPointerReference = NULL;
2789-
2790-
{
2791-
TR::VMAccessCriticalSection knownObjectTableGetIndex(fe);
2792-
index = knot->getOrCreateIndex(objectPointer);
2793-
objectPointerReference = knot->getPointerLocation(index);
2794-
}
2795-
2796-
client->write(response, index, objectPointerReference);
2797-
}
2798-
break;
27992792
case MessageType::KnownObjectTable_getOrCreateIndexAt:
28002793
{
28012794
uintptr_t *objectPointerReferenceServerQuery = std::get<0>(client->getRecvData<uintptr_t*>());
28022795
TR::KnownObjectTable::Index index = knot->getOrCreateIndexAt(objectPointerReferenceServerQuery);
28032796
client->write(response, index, knot->getPointerLocation(index));
28042797
}
28052798
break;
2806-
case MessageType::KnownObjectTable_getPointer:
2807-
{
2808-
TR::KnownObjectTable::Index knotIndex = std::get<0>(client->getRecvData<TR::KnownObjectTable::Index>());
2809-
uintptr_t objectPointer = 0;
2810-
2811-
{
2812-
TR::VMAccessCriticalSection knownObjectTableGetPointer(fe);
2813-
objectPointer = knot->getPointer(knotIndex);
2814-
}
2815-
2816-
client->write(response, objectPointer);
2817-
}
2818-
break;
28192799
case MessageType::KnownObjectTable_getExistingIndexAt:
28202800
{
28212801
uintptr_t *objectPointerReference = std::get<0>(client->getRecvData<uintptr_t*>());

runtime/compiler/env/J9KnownObjectTable.cpp

Lines changed: 58 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@
3838

3939
J9::KnownObjectTable::KnownObjectTable(TR::Compilation *comp) :
4040
OMR::KnownObjectTableConnector(comp),
41-
_references(comp->trMemory()),
41+
_objectInfos(comp->trMemory()),
4242
_stableArrayRanks(comp->trMemory())
4343
{
44-
_references.add(NULL); // Reserve index zero for NULL
44+
_objectInfos.add(ObjectInfo()); // Reserve index zero for NULL
4545
}
4646

4747

@@ -54,7 +54,7 @@ J9::KnownObjectTable::self()
5454
TR::KnownObjectTable::Index
5555
J9::KnownObjectTable::getEndIndex()
5656
{
57-
return _references.size();
57+
return _objectInfos.size();
5858
}
5959

6060

@@ -73,45 +73,23 @@ J9::KnownObjectTable::getOrCreateIndex(uintptr_t objectPointer)
7373

7474
uint32_t nextIndex = self()->getEndIndex();
7575
#if defined(J9VM_OPT_JITSERVER)
76-
if (self()->comp()->isOutOfProcessCompilation())
77-
{
78-
TR_ASSERT_FATAL(false, "It is not safe to call getOrCreateIndex() at the server. The object pointer could have become stale at the client.");
79-
auto stream = TR::CompilationInfo::getStream();
80-
stream->write(JITServer::MessageType::KnownObjectTable_getOrCreateIndex, objectPointer);
81-
auto recv = stream->read<TR::KnownObjectTable::Index, uintptr_t *>();
82-
83-
TR::KnownObjectTable::Index index = std::get<0>(recv);
84-
uintptr_t *objectReferenceLocation = std::get<1>(recv);
85-
TR_ASSERT_FATAL(index <= nextIndex, "The KOT index %d at the client is greater than the KOT index %d at the server", index, nextIndex);
86-
87-
if (index < nextIndex)
88-
{
89-
return index;
90-
}
91-
else
92-
{
93-
updateKnownObjectTableAtServer(index, objectReferenceLocation);
94-
}
95-
}
96-
else
76+
TR_ASSERT_FATAL(!self()->comp()->isOutOfProcessCompilation(), "It is not safe to call getOrCreateIndex() at the server. The object pointer could have become stale at the client.");
9777
#endif /* defined(J9VM_OPT_JITSERVER) */
98-
{
99-
TR_J9VMBase *fej9 = (TR_J9VMBase *)(self()->fe());
100-
TR_ASSERT(fej9->haveAccess(), "Must haveAccess in J9::KnownObjectTable::getOrCreateIndex");
101-
102-
// Search for existing matching entry
103-
//
104-
for (uint32_t i = 1; i < nextIndex; i++)
105-
if (*_references.element(i) == objectPointer)
106-
return i;
107-
108-
// No luck -- allocate a new one
109-
//
110-
J9VMThread *thread = getJ9VMThreadFromTR_VM(self()->fe());
111-
TR_ASSERT(thread, "assertion failure");
112-
_references.setSize(nextIndex+1);
113-
_references[nextIndex] = (uintptr_t*)thread->javaVM->internalVMFunctions->j9jni_createLocalRef((JNIEnv*)thread, (j9object_t)objectPointer);
114-
}
78+
TR_J9VMBase *fej9 = (TR_J9VMBase *)(self()->fe());
79+
TR_ASSERT(fej9->haveAccess(), "Must haveAccess in J9::KnownObjectTable::getOrCreateIndex");
80+
81+
// Search for existing matching entry
82+
//
83+
for (uint32_t i = 1; i < nextIndex; i++)
84+
if (*(_objectInfos.element(i)._jniReference) == objectPointer)
85+
return i;
86+
87+
// No luck -- allocate a new one
88+
//
89+
J9VMThread *thread = getJ9VMThreadFromTR_VM(self()->fe());
90+
TR_ASSERT(thread, "assertion failure");
91+
_objectInfos.setSize(nextIndex+1);
92+
_objectInfos[nextIndex]._jniReference = (uintptr_t*)thread->javaVM->internalVMFunctions->j9jni_createLocalRef((JNIEnv*)thread, (j9object_t)objectPointer);
11593

11694
return nextIndex;
11795
}
@@ -135,7 +113,7 @@ J9::KnownObjectTable::getOrCreateIndexAt(uintptr_t *objectReferenceLocation)
135113
#if defined(J9VM_OPT_JITSERVER)
136114
if (self()->comp()->isOutOfProcessCompilation())
137115
{
138-
auto stream = TR::CompilationInfo::getStream();
116+
auto stream = self()->comp()->getStream();
139117
stream->write(JITServer::MessageType::KnownObjectTable_getOrCreateIndexAt, objectReferenceLocation);
140118
auto recv = stream->read<TR::KnownObjectTable::Index, uintptr_t *>();
141119

@@ -204,31 +182,49 @@ J9::KnownObjectTable::getPointer(Index index)
204182
else
205183
{
206184
#if defined(J9VM_OPT_JITSERVER)
207-
if (self()->comp()->isOutOfProcessCompilation())
208-
{
209-
TR_ASSERT_FATAL(false, "It is not safe to call getPointer() at the server. The object pointer could have become stale at the client.");
210-
auto stream = TR::CompilationInfo::getStream();
211-
stream->write(JITServer::MessageType::KnownObjectTable_getPointer, index);
212-
return std::get<0>(stream->read<uintptr_t>());
213-
}
214-
else
185+
TR_ASSERT_FATAL(!self()->comp()->isOutOfProcessCompilation(), "It is not safe to call getPointer() at the server. The object pointer could have become stale at the client.");
215186
#endif /* defined(J9VM_OPT_JITSERVER) */
216-
{
217-
TR_J9VMBase *fej9 = (TR_J9VMBase *)(self()->fe());
218-
TR_ASSERT(fej9->haveAccess(), "Must haveAccess in J9::KnownObjectTable::getPointer");
219-
return *self()->getPointerLocation(index);
220-
}
187+
TR_J9VMBase *fej9 = (TR_J9VMBase *)(self()->fe());
188+
TR_ASSERT(fej9->haveAccess(), "Must haveAccess in J9::KnownObjectTable::getPointer");
189+
return *self()->getPointerLocation(index);
221190
}
222191
}
223192

224193

225194
uintptr_t *
226195
J9::KnownObjectTable::getPointerLocation(Index index)
227196
{
228-
TR_ASSERT(index != UNKNOWN && 0 <= index && index < _references.size(), "getPointerLocation(%d): index must be in range 0..%d", (int)index, _references.size());
229-
return _references[index];
197+
TR_ASSERT(index != UNKNOWN && 0 <= index && index < _objectInfos.size(), "getPointerLocation(%d): index must be in range 0..%d", (int)index, _objectInfos.size());
198+
return _objectInfos[index]._jniReference;
199+
}
200+
201+
void
202+
J9::KnownObjectTable::setObjectInfoFields(Index index, const ObjectInfo& objInfoSource)
203+
{
204+
ObjectInfo &objInfo = _objectInfos[index];
205+
TR_ASSERT_FATAL(objInfo._jniReference, "Index %d from known object table must have a valid jniReference", index);
206+
TR_ASSERT_FATAL(objInfo._jniReference == objInfoSource._jniReference, "The _jniReference for source and destination do not match. Index=%d %p != %p", index, objInfo._jniReference, objInfoSource._jniReference);
207+
objInfo = objInfoSource;
230208
}
231209

210+
// Delete all the JNI references to the known objects tracked by the table
211+
void
212+
J9::KnownObjectTable::freeKnownObjectTable()
213+
{
214+
#if defined(J9VM_OPT_JITSERVER)
215+
if (comp()->isOutOfProcessCompilation())
216+
return;
217+
#endif /* defined(J9VM_OPT_JITSERVER) */
218+
219+
TR_J9VMBase *fej9 = (TR_J9VMBase *)(self()->fe());
220+
J9VMThread *thread = fej9->vmThread();
221+
TR_ASSERT(thread, "assertion failure");
222+
223+
TR::VMAccessCriticalSection freeKnownObjectTable(fej9);
224+
uint32_t nextIndex = self()->getEndIndex();
225+
for (uint32_t i = 1; i < nextIndex; i++)
226+
thread->javaVM->internalVMFunctions->j9jni_deleteLocalRef((JNIEnv*)thread, (jobject)_objectInfos.element(i)._jniReference);
227+
}
232228

233229
#if defined(J9VM_OPT_JITSERVER)
234230
void
@@ -244,15 +240,14 @@ J9::KnownObjectTable::updateKnownObjectTableAtServer(Index index, uintptr_t *obj
244240

245241
if (index == nextIndex)
246242
{
247-
_references.setSize(nextIndex+1);
248-
_references[nextIndex] = objectReferenceLocationClient;
243+
_objectInfos.setSize(nextIndex+1);
244+
_objectInfos[nextIndex]._jniReference = objectReferenceLocationClient;
249245
}
250246
else if (index < nextIndex)
251247
{
252-
TR_ASSERT((objectReferenceLocationClient == _references[index]),
248+
TR_ASSERT_FATAL((objectReferenceLocationClient == _objectInfos[index]._jniReference),
253249
"comp %p: server _references[%d]=%p is not the same as the client _references[%d]=%p (total size = %u)",
254-
self()->comp(), index, _references[index], index, objectReferenceLocationClient, nextIndex);
255-
_references[index] = objectReferenceLocationClient;
250+
self()->comp(), index, _objectInfos[index]._jniReference, index, objectReferenceLocationClient, nextIndex);
256251
}
257252
else
258253
{
@@ -403,7 +398,7 @@ J9::KnownObjectTable::dumpTo(OMR::Logger *log, TR::Compilation *comp)
403398
#if defined(J9VM_OPT_JITSERVER)
404399
if (comp->isOutOfProcessCompilation())
405400
{
406-
auto stream = TR::CompilationInfo::getStream();
401+
auto stream = comp->getStream();
407402
stream->write(JITServer::MessageType::KnownObjectTable_getKnownObjectTableDumpInfo, JITServer::Void());
408403

409404
auto recv = stream->read<std::vector<TR_KnownObjectTableDumpInfo>>();

runtime/compiler/env/J9KnownObjectTable.hpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,25 @@ namespace J9
7474

7575
class OMR_EXTENSIBLE KnownObjectTable : public OMR::KnownObjectTableConnector
7676
{
77+
public:
78+
// Note: the _jniReference field is always populated for a valid known object index.
79+
// The other fields will be populated on demand. If _clazz is populated (non NULL),
80+
// we expect the other fields to be populated too.
81+
struct ObjectInfo
82+
{
83+
uintptr_t *_jniReference;
84+
TR_OpaqueClassBlock *_clazz;
85+
TR_OpaqueClassBlock *_jlClass;
86+
bool _isFixedJavaLangClass;
87+
bool _isString;
88+
ObjectInfo() : _jniReference(NULL), _clazz(NULL), _jlClass(NULL), _isFixedJavaLangClass(false), _isString(false) {}
89+
};
7790
friend class ::TR_J9VMBase;
7891
friend class Compilation;
79-
TR_Array<uintptr_t*> _references;
92+
private:
93+
TR_Array<ObjectInfo> _objectInfos;
8094
TR_Array<int32_t> _stableArrayRanks;
8195

82-
8396
public:
8497
TR_ALLOC(TR_Memory::FrontEnd);
8598

@@ -106,6 +119,11 @@ class OMR_EXTENSIBLE KnownObjectTable : public OMR::KnownObjectTableConnector
106119
Index getExistingIndexAt(uintptr_t *objectReferenceLocation);
107120

108121
uintptr_t getPointer(Index index);
122+
struct ObjectInfo getObjectInfo(Index index) const { return _objectInfos[index]; }
123+
124+
void setObjectInfoFields(Index index, const ObjectInfo& objInfo);
125+
126+
void freeKnownObjectTable();
109127

110128
#if defined(J9VM_OPT_JITSERVER)
111129
void updateKnownObjectTableAtServer(Index index, uintptr_t *objectReferenceLocationClient, bool isArrayWithConstantElements = false);

runtime/compiler/env/VMJ9.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,6 +1294,73 @@ TR_J9VMBase::getObjectClassInfoFromObjectReferenceLocation(TR::Compilation *comp
12941294
return ci;
12951295
}
12961296

1297+
TR::KnownObjectTable::ObjectInfo
1298+
TR_J9VMBase::getObjClassInfoFromKnotIndexNoCaching(TR::Compilation *comp, TR::KnownObjectTable::Index knotIndex)
1299+
{
1300+
TR::KnownObjectTable::ObjectInfo retrievedObjInfo;
1301+
TR::KnownObjectTable *knot = comp->getKnownObjectTable();
1302+
TR::KnownObjectTable::ObjectInfo existingObjInfo = knot->getObjectInfo(knotIndex);
1303+
1304+
// Retrieve the data from the VM
1305+
TR::VMAccessCriticalSection vmAccess(comp);
1306+
1307+
uintptr_t objectReference = knot->getPointer(knotIndex);
1308+
retrievedObjInfo._jniReference = existingObjInfo._jniReference;
1309+
retrievedObjInfo._clazz = getObjectClass(objectReference);
1310+
retrievedObjInfo._isString = isString(retrievedObjInfo._clazz);
1311+
retrievedObjInfo._jlClass = getClassClassPointer(retrievedObjInfo._clazz);
1312+
retrievedObjInfo._isFixedJavaLangClass = (retrievedObjInfo._jlClass == retrievedObjInfo._clazz);
1313+
if (retrievedObjInfo._isFixedJavaLangClass)
1314+
{
1315+
// A FixedClass constraint means something different
1316+
// when the class happens to be java/lang/Class.
1317+
// Must add constraints pertaining to the class that
1318+
// the java/lang/Class object represents.
1319+
retrievedObjInfo._clazz = getClassFromJavaLangClass(objectReference);
1320+
}
1321+
return retrievedObjInfo;
1322+
}
1323+
1324+
TR::KnownObjectTable::ObjectInfo
1325+
TR_J9VMBase::getObjClassInfoFromKnotIndex(TR::Compilation *comp, TR::KnownObjectTable::Index knotIndex)
1326+
{
1327+
TR::KnownObjectTable *knot = comp->getKnownObjectTable();
1328+
// Check to see whether the known object entry already holds the desired info.
1329+
// If so, return the cached version. Otherwise, fetch the info and cache it.
1330+
TR::KnownObjectTable::ObjectInfo existingObjInfo = knot->getObjectInfo(knotIndex);
1331+
TR::KnownObjectTable::ObjectInfo answerObjInfo;
1332+
TR_ASSERT_FATAL(existingObjInfo._jniReference, "The jniReference is missing for knotIndex %d", knotIndex);
1333+
if (comp->getOption(TR_DisableKnownObjectTableCaching))
1334+
{
1335+
answerObjInfo = getObjClassInfoFromKnotIndexNoCaching(comp, knotIndex);
1336+
}
1337+
else // Caching is enabled
1338+
{
1339+
if (existingObjInfo._clazz == NULL) // Not yet cached
1340+
{
1341+
answerObjInfo = getObjClassInfoFromKnotIndexNoCaching(comp, knotIndex);
1342+
knot->setObjectInfoFields(knotIndex, answerObjInfo); // populate cache
1343+
}
1344+
else // Reply with information from the cache
1345+
{
1346+
if (comp->getOption(TR_EnableKnownObjectTableCachingVerification))
1347+
{
1348+
TR::KnownObjectTable::ObjectInfo retrievedObjInfo = getObjClassInfoFromKnotIndexNoCaching(comp, knotIndex);
1349+
TR_ASSERT_FATAL(existingObjInfo._clazz == retrievedObjInfo._clazz, "_clazz discrepancy");
1350+
TR_ASSERT_FATAL(existingObjInfo._jlClass == retrievedObjInfo._jlClass, "_jlClazz discrepancy");
1351+
TR_ASSERT_FATAL(existingObjInfo._isString == retrievedObjInfo._isString, "_isString discrepancy");
1352+
TR_ASSERT_FATAL(existingObjInfo._isFixedJavaLangClass == retrievedObjInfo._isFixedJavaLangClass, "_isFixedJavaLangClass discrepancy");
1353+
TR_ASSERT_FATAL(existingObjInfo._jniReference == retrievedObjInfo._jniReference, "_jniReference discrepancy");
1354+
}
1355+
answerObjInfo = existingObjInfo;
1356+
}
1357+
}
1358+
1359+
J9::ConstProvenanceGraph *cpg = comp->constProvenanceGraph();
1360+
cpg->addEdge(cpg->knownObject(knotIndex), answerObjInfo._clazz);
1361+
return answerObjInfo;
1362+
}
1363+
12971364
uintptr_t
12981365
TR_J9VMBase::getReferenceFieldAtAddress(uintptr_t fieldAddress)
12991366
{

0 commit comments

Comments
 (0)