ArrayPool.java
package com.morphiqlabs.wavelet.internal;
import com.morphiqlabs.wavelet.util.ThreadLocalManager;
import java.util.concurrent.ConcurrentLinkedDeque;
/**
* Thread-safe object pool for reusable double arrays.
* Optimized for small signal processing to reduce GC pressure.
*
* <p>This pool maintains arrays of common power-of-2 sizes used in
* wavelet transforms. Arrays are borrowed and returned to avoid
* frequent allocations during batch processing.</p>
*/
final class ArrayPool {
// Common signal sizes for financial time series
private static final int[] POOL_SIZES = {32, 64, 128, 256, 512, 1024};
private static final int MAX_ARRAYS_PER_SIZE = 4; // Per thread
// Thread-local pools to avoid contention - managed by ThreadLocalManager
private static final ThreadLocalManager.ManagedThreadLocal<Pool> THREAD_LOCAL_POOL =
ThreadLocalManager.withInitial(Pool::new);
private ArrayPool() {
// Prevent instantiation
}
/**
* Borrows an array of the specified size.
* If no pooled array is available, allocates a new one.
*
* @param size the required array size
* @return a double array of the specified size
*/
public static double[] borrow(int size) {
return THREAD_LOCAL_POOL.get().borrow(size);
}
/**
* Returns an array to the pool for reuse.
* The array is cleared before being pooled.
*
* @param array the array to return
*/
public static void release(double[] array) {
if (array != null) {
THREAD_LOCAL_POOL.get().release(array);
}
}
/**
* Clears all arrays from the current thread's pool.
* Useful for explicit memory management.
*/
public static void clear() {
THREAD_LOCAL_POOL.get().clear();
}
/**
* Thread-local pool implementation.
*/
private static class Pool {
@SuppressWarnings("unchecked")
private final ConcurrentLinkedDeque<double[]>[] pools;
@SuppressWarnings({"unchecked", "rawtypes"})
Pool() {
pools = new ConcurrentLinkedDeque[POOL_SIZES.length];
for (int i = 0; i < POOL_SIZES.length; i++) {
pools[i] = new ConcurrentLinkedDeque<double[]>();
}
}
double[] borrow(int size) {
int poolIndex = getPoolIndex(size);
if (poolIndex >= 0) {
double[] array = pools[poolIndex].poll();
if (array != null) {
return array;
}
}
// No pooled array available, allocate new
return new double[size];
}
void release(double[] array) {
if (array == null) return;
int poolIndex = getPoolIndex(array.length);
if (poolIndex >= 0) {
// Clear the array before pooling
java.util.Arrays.fill(array, 0.0);
// Only pool if we haven't exceeded the limit
if (pools[poolIndex].size() < MAX_ARRAYS_PER_SIZE) {
pools[poolIndex].offer(array);
}
}
// Arrays of non-standard sizes are not pooled
}
void clear() {
for (ConcurrentLinkedDeque<double[]> pool : pools) {
pool.clear();
}
}
private int getPoolIndex(int size) {
for (int i = 0; i < POOL_SIZES.length; i++) {
if (POOL_SIZES[i] == size) {
return i;
}
}
return -1;
}
}
}