Skip to content

Commit 5c98d28

Browse files
committed
Use custom allocator for JITServer client MessageBuffers
A global, reference-counted allocator is now created at JITServer clients that is used to allocate the memory used by the internal MessageBuffer storage buffer during client/server communication. This allocator's memory segments are disclaimed if the number of connections to the server drops to zero, and the compiler will also periodically attempt to destroy the allocator entirely. Signed-off-by: Christian Despres <[email protected]>
1 parent 9cbb1d5 commit 5c98d28

File tree

6 files changed

+104
-12
lines changed

6 files changed

+104
-12
lines changed

runtime/compiler/control/HookedByTheJit.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5320,6 +5320,11 @@ static void jitStateLogic(J9JITConfig * jitConfig, TR::CompilationInfo * compInf
53205320
static char *disableIdleRATCleanup = feGetEnv("TR_disableIdleRATCleanup");
53215321
if (disableIdleRATCleanup == NULL)
53225322
persistentInfo->getRuntimeAssumptionTable()->reclaimMarkedAssumptionsFromRAT(-1);
5323+
5324+
#if defined(J9VM_OPT_JITSERVER)
5325+
if (compInfo->getMethodQueueSize() == 0)
5326+
JITServer::MessageBuffer::tryFreePersistentAllocator();
5327+
#endif /* defined(J9VM_OPT_JITSERVER) */
53235328
}
53245329

53255330
// Logic related to IdleCPU exploitation

runtime/compiler/control/rossa.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,6 +1792,9 @@ onLoadInternal(
17921792
}
17931793
}
17941794

1795+
if (compInfo->getPersistentInfo()->getRemoteCompilationMode() != JITServer::NONE)
1796+
JITServer::MessageBuffer::initTotalBuffersMonitor();
1797+
17951798
if (compInfo->getPersistentInfo()->getRemoteCompilationMode() == JITServer::SERVER)
17961799
{
17971800
JITServer::CommunicationStream::initConfigurationFlags();

runtime/compiler/env/PersistentAllocator.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,21 @@ PersistentAllocator::disclaimAllSegments()
757757
#endif // LINUX
758758
return numSegDisclaimed;
759759
}
760+
761+
void
762+
TR::PersistentAllocator::adviseDontNeedSegments()
763+
{
764+
#ifdef LINUX
765+
j9thread_monitor_enter(_segmentMonitor);
766+
for (auto segmentIterator = _segments.begin(); segmentIterator != _segments.end(); ++segmentIterator)
767+
{
768+
J9MemorySegment &segment = *segmentIterator;
769+
size_t segLength = segment.heapTop - segment.heapBase;
770+
madvise(segment.heapBase, segLength, MADV_DONTNEED);
771+
}
772+
j9thread_monitor_exit(_segmentMonitor);
773+
#endif
774+
}
760775
} // namespace J9
761776

762777
void *

runtime/compiler/env/PersistentAllocator.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ class PersistentAllocator
7777
int disclaimAllSegments();
7878
int getNumSegments() const { return _numSegments; }
7979

80+
// Issue MADV_DONTNEED on the segments allocated by this allocator. This has the effect
81+
// of zeroing out that memory if it has no persistent backing (see madvise(2)), so you must
82+
// be sure that these segments are not actually in use in that case.
83+
void adviseDontNeedSegments();
84+
8085
private:
8186

8287
// Persistent block header

runtime/compiler/net/MessageBuffer.cpp

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,53 @@
2121
*******************************************************************************/
2222

2323
#include "net/MessageBuffer.hpp"
24+
#include "infra/CriticalSection.hpp"
25+
#include "env/VerboseLog.hpp"
26+
#include "control/Options.hpp"
2427
#include <cstring>
2528

2629
namespace JITServer
2730
{
31+
32+
TR::Monitor *MessageBuffer::_totalBuffersMonitor = NULL;
33+
int MessageBuffer::_totalBuffers = 0;
34+
TR::PersistentAllocator *MessageBuffer::_allocator = NULL;
35+
2836
MessageBuffer::MessageBuffer() :
29-
_capacity(INITIAL_BUFFER_SIZE),
30-
_allocator(TR::Compiler->persistentGlobalAllocator())
37+
_capacity(INITIAL_BUFFER_SIZE)
3138
{
39+
OMR::CriticalSection cs(getTotalBuffersMonitor());
40+
41+
if (!_allocator)
42+
{
43+
if (J9::PersistentInfo::_remoteCompilationMode == JITServer::CLIENT)
44+
{
45+
uint32_t memoryType = MEMORY_TYPE_VIRTUAL; // Force the usage of mmap for allocation
46+
TR::PersistentAllocatorKit kit(1 << 20/*1 MB*/, *TR::Compiler->javaVM, memoryType);
47+
_allocator = new (TR::Compiler->rawAllocator) TR::PersistentAllocator(kit);
48+
}
49+
else
50+
{
51+
_allocator = &TR::Compiler->persistentGlobalAllocator();
52+
}
53+
}
54+
3255
_storage = allocateMemory(_capacity);
3356
if (!_storage)
3457
throw std::bad_alloc();
3558
_curPtr = _storage;
59+
_totalBuffers++;
60+
}
61+
62+
MessageBuffer::~MessageBuffer()
63+
{
64+
OMR::CriticalSection cs(getTotalBuffersMonitor());
65+
66+
freeMemory(_storage);
67+
_totalBuffers--;
68+
69+
if ((0 == _totalBuffers) && (J9::PersistentInfo::_remoteCompilationMode == JITServer::CLIENT))
70+
_allocator->adviseDontNeedSegments();
3671
}
3772

3873
void
@@ -79,7 +114,7 @@ MessageBuffer::writeData(const void *dataStart, uint32_t dataSize, uint8_t paddi
79114
_curPtr += dataSize + paddingSize;
80115
return offset(data);
81116
}
82-
117+
83118
uint8_t
84119
MessageBuffer::alignCurrentPositionOn64Bit()
85120
{
@@ -108,5 +143,22 @@ MessageBuffer::computeRequiredCapacity(uint32_t requiredSize)
108143
extendedCapacity *= 2;
109144
return extendedCapacity;
110145
}
111-
};
112146

147+
void
148+
MessageBuffer::tryFreePersistentAllocator()
149+
{
150+
if (J9::PersistentInfo::_remoteCompilationMode != JITServer::CLIENT)
151+
return;
152+
153+
OMR::CriticalSection cs(getTotalBuffersMonitor());
154+
155+
if ((_totalBuffers != 0) || (_allocator == NULL))
156+
return;
157+
158+
_allocator->~PersistentAllocator();
159+
TR::Compiler->rawAllocator.deallocate(_allocator);
160+
_allocator = NULL;
161+
if (TR::Options::getVerboseOption(TR_VerbosePerformance))
162+
TR_VerboseLog::writeLineLocked(TR_Vlog_PERF, "Freed message buffer storage allocator");
163+
}
164+
};

runtime/compiler/net/MessageBuffer.hpp

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "env/TRMemory.hpp"
2828
#include "OMR/Bytes.hpp" // for alignNoCheck
2929
#include "env/CompilerEnv.hpp"
30+
#include "infra/Monitor.hpp"
3031

3132
namespace JITServer
3233
{
@@ -45,16 +46,17 @@ namespace JITServer
4546
4647
Variable _curPtr defines the boundary of the current data. Reading/writing to/from buffer
4748
will always advance the pointer.
49+
50+
A shared, reference-counted persistent allocator is used for all message buffers at a JITServer client.
51+
The freed persistent memory tracked by the allocator is disclaimed when the last MessageBuffer is destroyed,
52+
and the allocator itself can be destroyed with tryFreePersistentAllocator() if there are no active connections
53+
to a server. Servers use the persistent global allocator.
4854
*/
4955
class MessageBuffer
5056
{
5157
public:
5258
MessageBuffer();
53-
54-
~MessageBuffer()
55-
{
56-
freeMemory(_storage);
57-
}
59+
~MessageBuffer();
5860

5961

6062
/**
@@ -204,18 +206,28 @@ class MessageBuffer
204206

205207
uint32_t getCapacity() const { return _capacity; }
206208

209+
// Must be called before any client-server communication takes place
210+
static void initTotalBuffersMonitor() { _totalBuffersMonitor = TR::Monitor::create("JIT-JITServerTotalBuffersMonitor"); }
211+
212+
// Try to free the custom persistent allocator for message buffers. This method does nothing
213+
// if the JVM is not in client mode, or if there is at least one active message buffer.
214+
static void tryFreePersistentAllocator();
207215

208216
private:
209217
static const size_t INITIAL_BUFFER_SIZE = 32768; // Initial buffer size is 32K
210218
uint32_t offset(char *addr) const { return addr - _storage; }
211-
char *allocateMemory(uint32_t capacity) { return static_cast<char *>(_allocator.allocate(capacity)); }
212-
void freeMemory(char *storage) { _allocator.deallocate(storage); }
219+
char *allocateMemory(uint32_t capacity) { return static_cast<char *>(_allocator->allocate(capacity)); }
220+
void freeMemory(char *storage) { _allocator->deallocate(storage); }
213221
uint32_t computeRequiredCapacity(uint32_t requiredSize);
222+
static TR::Monitor *getTotalBuffersMonitor() { return _totalBuffersMonitor; }
214223

215224
uint32_t _capacity;
216225
char *_storage;
217226
char *_curPtr;
218-
TR::PersistentAllocator &_allocator;
227+
228+
static TR::Monitor *_totalBuffersMonitor;
229+
static int _totalBuffers;
230+
static TR::PersistentAllocator *_allocator;
219231
};
220232
};
221233
#endif

0 commit comments

Comments
 (0)