Skip to content

Commit

Permalink
nfs: calculate desired memory fraction for correct memory allocation
Browse files Browse the repository at this point in the history
Motivation:
Grizzly memory management uses heap memory size fraction even if direct
memory is used.

eclipse-ee4j/grizzly#2201

Moreover, for each memory slice (a memory segment per thread) allocates
at least x16 chunks, which ends up at 16MB per slice (with 1MB chunk)

Modification:
as dCache needs `2 * #Cores * maxIObuf` memory, pre-calculate the
required amount of direct memory and the corresponding fraction
in relation to heap. Initialise the memory pool with 1/16 of the
desired slice size to compensate memory allocator internal x16
increase.

Result:
dCache starts with 256m of direct memory (we still have xroot mover)

Fixes: #7522
Acked-by: Svenja Meyer
Acked-by: Lea Morschel
Target: master, 9.2
Require-book: no
Require-notes: yes
(cherry picked from commit 8f3b984)
Signed-off-by: Tigran Mkrtchyan <tigran.mkrtchyan@desy.de>
  • Loading branch information
kofemann authored and mksahakyan committed Mar 11, 2024
1 parent f51fcde commit 58cc1f3
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 4 deletions.
Expand Up @@ -57,7 +57,7 @@ public void process(CompoundContext context, nfs_resop4 result) {
Buffer[] bufs = bArray.getArray();
int size = bArray.size();

for(int i = 0; i < size; i++) {
for(int i = 0; bytesToRead > 0 && i < size; i++) {

ByteBuffer directChunk = bufs[i].toByteBuffer();
directChunk.clear().limit(Math.min(directChunk.capacity(), bytesToRead));
Expand Down
Expand Up @@ -161,17 +161,31 @@ public class NfsTransferService

private CellAddressCore _cellAddress;

// This is a workaround for the issue with the grizzly allocator.
// (which uses a fraction of heap memory for direct buffers, instead of configured direct memory limit
// See: https://github.com/eclipse-ee4j/grizzly/issues/2201

// as we know in advance how much memory is going to be used, we can pre-calculate the desired fraction.
// The expected direct buffer allocation is `<chunk size> * <expected concurrency>` (with an assumption,
// that we use only one memory pool, i.g. no grow).

private final int expectedConcurrency = GrizzlyUtils.getDefaultWorkerPoolSize();
private final int allocationChunkSize = ByteUnit.MiB.toBytes(1); // one pool with 1MB chunks (max NFS rsize)
private final float heapFraction = (allocationChunkSize * expectedConcurrency) / (float) Runtime.getRuntime().maxMemory();

/**
* Buffer pool for IO operations.
* One pool with 1MB chunks (max NFS rsize).
*/
private final MemoryManager<? extends Buffer> pooledBufferAllocator =
new PooledMemoryManager(// one pool with 1MB chunks (max NFS rsize)
ByteUnit.MiB.toBytes(1), // base chunk size
allocationChunkSize / 16, // Grizzly allocates at least 16 chunks per slice,
// for 1MB buffers 16MB in total.
// Pass 1/16 of the desired buffer size to compensate the over commitment.
1, // number of pools
2, // grow facter per pool, ignored, see above
GrizzlyUtils.getDefaultWorkerPoolSize(), // expected concurrency
PooledMemoryManager.DEFAULT_HEAP_USAGE_PERCENTAGE,
expectedConcurrency, // expected concurrency
heapFraction, // fraction of heap memory to use for direct buffers
PooledMemoryManager.DEFAULT_PREALLOCATED_BUFFERS_PERCENTAGE,
true // direct buffers
);
Expand Down

0 comments on commit 58cc1f3

Please sign in to comment.