Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ public final class ExtendedRandom {
OCKContext ockContext;
Copy link
Member

Choose a reason for hiding this comment

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

Do we need any additional testing in our test buckets to test randoms from the cache right around the boundary limits for our cache?

final long ockPRNGContextId;

// 128KB cache ?
private static final int MEGABYTE_CACHE_SIZE = 128 * 1024;
Copy link
Member

Choose a reason for hiding this comment

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

Can this be a configurable size. By default one megabyte seems reasonable to me given your findings.


// 16KB threshold ?
private static final int BYPASS_THRESHOLD = 16 * 1024;
Copy link
Member

Choose a reason for hiding this comment

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

What is the threshold trying to accomplish? Seems like if the user wants more randoms then is in the cache then we would then make a trip to the native layer to fill the cache at that time and then return the bytes to the caller. This would save some complexity for now.


private byte[] megaByteCache;
private int cachePos; // Next unread index in cache
private int megaByteCacheLength;

public static ExtendedRandom getInstance(OCKContext ockContext, String algName, OpenJCEPlusProvider provider)
throws OCKException {
if (ockContext == null) {
Expand Down Expand Up @@ -45,9 +55,42 @@ public synchronized void nextBytes(byte[] bytes) throws OCKException {
if (bytes == null) {
throw new IllegalArgumentException("bytes is null");
}
int len = bytes.length;
if (len == 0) {
return;
}

if (bytes.length > 0) {
// 1) LARGE REQUEST BYPASS:
// Fill destination directly to avoid cache->dest copy cost.
if (len >= BYPASS_THRESHOLD) {
NativeInterface.EXTRAND_nextBytes(ockContext.getId(), ockPRNGContextId, bytes);

// Invalidate cache so next small request refills fresh.
cachePos = megaByteCacheLength = 0;
return;
}

// 2) SMALL/MEDIUM REQUEST:
// Serve from cache, refilling as needed.
int outPos = 0;
int remaining = len;

while (remaining > 0) {
int available = megaByteCacheLength - cachePos;

// If cache is empty (or not initialized), refill it.
if (available <= 0) {
refillMegaByteCache();
available = megaByteCacheLength - cachePos;
}

// Copy as much as we can from cache into output.
int toCopy = Math.min(available, remaining);
System.arraycopy(megaByteCache, cachePos, bytes, outPos, toCopy);

cachePos += toCopy;
outPos += toCopy;
remaining -= toCopy;
}
}

Expand Down Expand Up @@ -75,4 +118,16 @@ private Runnable cleanOCKResources(long ockPRNGContextId, OCKContext ockContext)
}
};
}

private void refillMegaByteCache() throws OCKException {
if (megaByteCache == null) {
megaByteCache = new byte[MEGABYTE_CACHE_SIZE];
}

// Fill the entire cache from native.
NativeInterface.EXTRAND_nextBytes(ockContext.getId(), ockPRNGContextId, megaByteCache);

cachePos = 0;
megaByteCacheLength = megaByteCache.length;
}
}