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

Reduce number of VM_getReferenceSlotsInClass messages for JITServer #21123

Merged
merged 1 commit into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 60 additions & 6 deletions runtime/compiler/env/VMJ9Server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1628,16 +1628,70 @@ TR_J9ServerVM::reportHotField(int32_t reducedCpuUtil, J9Class* clazz, uint8_t fi
int32_t *
TR_J9ServerVM::getReferenceSlotsInClass(TR::Compilation *comp, TR_OpaqueClassBlock *clazz)
{
bool classIsCached = true;
// First check the cache
{
OMR::CriticalSection getRemoteROMClass(_compInfoPT->getClientData()->getROMMapMonitor());
auto it = _compInfoPT->getClientData()->getROMClassMap().find(reinterpret_cast<J9Class *>(clazz));
if (it != _compInfoPT->getClientData()->getROMClassMap().end())
{
// 'clazz' is cached. How about the reference slot info for this class?
auto &refSlotsCache = it->second._referenceSlotsInClass;
if (refSlotsCache.size() > 0)
{
// I have the reference fields cached. Copy them out.
if (refSlotsCache.size() == 1) // Last one is a 0 marker, i.e. end-of-list
return NULL; // 'clazz' has no reference fields
size_t sizeInBytes = refSlotsCache.size() * sizeof(int32_t);
// Ideally we would return a pointer inside the vector (refSlotsCache.data()),
// but the danger is that the compiler might try to free it. It's safer just to copy the data.
int32_t *refSlots = (int32_t *)comp->trHeapMemory().allocate(sizeInBytes);
if (!refSlots)
throw std::bad_alloc();
memcpy(refSlots, refSlotsCache.data(), sizeInBytes);
return refSlots;
}
}
else // Class is not cached
{
// Don't try to cache 'clazz'. Just ask the client for the reference slots info.
classIsCached = false;
}
}
int32_t numRefSlots = 0;
int32_t *refSlots = NULL;
// Send a message to the client to retrieve the desired data
JITServer::ServerStream *stream = _compInfoPT->getMethodBeingCompiled()->_stream;
stream->write(JITServer::MessageType::VM_getReferenceSlotsInClass, clazz);
auto recv = stream->read<std::string>();
auto &slotsStr = std::get<0>(recv);
if (slotsStr.empty())
return NULL;
int32_t *refSlots = (int32_t *)comp->trHeapMemory().allocate(slotsStr.size());
if (!refSlots)
throw std::bad_alloc();
memcpy(refSlots, slotsStr.data(), slotsStr.size());
if (!slotsStr.empty())
{
refSlots = (int32_t *)comp->trHeapMemory().allocate(slotsStr.size());
if (!refSlots)
throw std::bad_alloc();
memcpy(refSlots, slotsStr.data(), slotsStr.size());
numRefSlots = slotsStr.size() / sizeof(int32_t) - 1; // Last entry is the NULL terminator
}

// If the class is cached, we can also cache the information about the reference slots.
if (classIsCached)
{
OMR::CriticalSection getRemoteROMClass(_compInfoPT->getClientData()->getROMMapMonitor());
auto it = _compInfoPT->getClientData()->getROMClassMap().find(reinterpret_cast<J9Class *>(clazz));
if (it != _compInfoPT->getClientData()->getROMClassMap().end())
{
auto &refSlotsCache = it->second._referenceSlotsInClass;
if (refSlots)
{
refSlotsCache.reserve(numRefSlots + 1);
for (int i = 0; i < numRefSlots; i++)
refSlotsCache.push_back(refSlots[i]);
}
// Add a 0 terminator for the sequence of reference slots.
refSlotsCache.push_back(0);
}
}
return refSlots;
}

Expand Down
3 changes: 2 additions & 1 deletion runtime/compiler/runtime/JITClientSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,8 @@ ClientSessionData::ClassInfo::ClassInfo(TR_PersistentMemory *persistentMemory) :
_fieldOrStaticDefiningClassCache(decltype(_fieldOrStaticDefiningClassCache)::allocator_type(persistentMemory->_persistentAllocator.get())),
_J9MethodNameCache(decltype(_J9MethodNameCache)::allocator_type(persistentMemory->_persistentAllocator.get())),
_isStableCache(decltype(_isStableCache)::allocator_type(persistentMemory->_persistentAllocator.get())),
_referencingClassLoaders(decltype(_referencingClassLoaders)::allocator_type(persistentMemory->_persistentAllocator.get()))
_referencingClassLoaders(decltype(_referencingClassLoaders)::allocator_type(persistentMemory->_persistentAllocator.get())),
_referenceSlotsInClass(decltype(_referenceSlotsInClass)::allocator_type(persistentMemory->_persistentAllocator.get()))
{
}

Expand Down
7 changes: 7 additions & 0 deletions runtime/compiler/runtime/JITClientSession.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,13 @@ class ClientSessionData
PersistentUnorderedMap<int32_t, J9MethodNameAndSignature> _J9MethodNameCache; // key is a cpIndex
PersistentUnorderedMap<int32_t, bool> _isStableCache; // Store the presence of the Stable annotation for the field indicated by a cpIndex
PersistentUnorderedSet<J9ClassLoader *> _referencingClassLoaders;
// The following vector caches information about the offsets of the reference slots in the class.
// An empty vector means I don't have any information yet.
// The information is encoded with a zero terminator element. Thus, if the vector contains
// exactly one element (which must be the 0 terminator), it means that the class contains no reference fields.
// This information is not collected when the class is sent from the client to server. Rather, it is
// populated when the server needs it.
PersistentVector<int32_t> _referenceSlotsInClass; // Array of N int32_t values. The last one has a value of 0.
}; // struct ClassInfo

/**
Expand Down