FinancialAnalysisObjectPool.java
package com.morphiqlabs.wavelet.cwt.finance;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Object pool for financial analysis to reduce GC pressure.
*
* <p>This pool manages reusable objects and arrays used in hot paths
* of financial analysis, significantly reducing memory allocation overhead.</p>
*/
public final class FinancialAnalysisObjectPool {
// Pool configuration
private static final int DEFAULT_MAX_POOL_SIZE = 16;
private static final int DEFAULT_ARRAY_SIZE = 1024;
// Pools for different object types
private final ConcurrentLinkedQueue<ArrayHolder> arrayPool;
private final ConcurrentLinkedQueue<TradingSignalBuilder> signalBuilderPool;
private final ConcurrentLinkedQueue<IntListBuilder> intListPool;
private final ConcurrentLinkedQueue<DoubleListBuilder> doubleListPool;
// Pool statistics
private final AtomicInteger arrayPoolHits = new AtomicInteger(0);
private final AtomicInteger arrayPoolMisses = new AtomicInteger(0);
private final int maxPoolSize;
private final int defaultArraySize;
/**
* Creates a pool with default configuration.
*/
public FinancialAnalysisObjectPool() {
this(DEFAULT_MAX_POOL_SIZE, DEFAULT_ARRAY_SIZE);
}
/**
* Creates a pool with custom configuration.
*
* @param maxPoolSize maximum objects to keep in each pool
* @param defaultArraySize default size for array allocations
*/
public FinancialAnalysisObjectPool(int maxPoolSize, int defaultArraySize) {
this.maxPoolSize = maxPoolSize;
this.defaultArraySize = defaultArraySize;
this.arrayPool = new ConcurrentLinkedQueue<>();
this.signalBuilderPool = new ConcurrentLinkedQueue<>();
this.intListPool = new ConcurrentLinkedQueue<>();
this.doubleListPool = new ConcurrentLinkedQueue<>();
// Pre-populate pools
for (int i = 0; i < maxPoolSize / 2; i++) {
arrayPool.offer(new ArrayHolder(defaultArraySize));
signalBuilderPool.offer(new TradingSignalBuilder());
intListPool.offer(new IntListBuilder());
doubleListPool.offer(new DoubleListBuilder());
}
}
/**
* Borrows a double array from the pool.
*
* @param minSize minimum required size
* @return array holder that must be returned to pool
*/
public ArrayHolder borrowArray(int minSize) {
ArrayHolder holder = arrayPool.poll();
if (holder == null) {
arrayPoolMisses.incrementAndGet();
holder = new ArrayHolder(Math.max(minSize, defaultArraySize));
} else {
arrayPoolHits.incrementAndGet();
if (holder.array.length < minSize) {
holder.array = new double[Math.max(minSize, defaultArraySize)];
}
holder.clear();
}
holder.setPool(this);
return holder;
}
/**
* Returns an array to the pool.
*
* @param holder the array holder to return
*/
public void returnArray(ArrayHolder holder) {
if (holder != null && arrayPool.size() < maxPoolSize) {
holder.clear();
arrayPool.offer(holder);
}
}
/**
* Borrows a trading signal builder from the pool.
*/
public TradingSignalBuilder borrowSignalBuilder() {
TradingSignalBuilder builder = signalBuilderPool.poll();
if (builder == null) {
return new TradingSignalBuilder();
}
builder.clear();
return builder;
}
/**
* Returns a signal builder to the pool.
*/
public void returnSignalBuilder(TradingSignalBuilder builder) {
if (builder != null && signalBuilderPool.size() < maxPoolSize) {
builder.clear();
signalBuilderPool.offer(builder);
}
}
/**
* Borrows an int list builder from the pool.
*/
public IntListBuilder borrowIntList() {
IntListBuilder builder = intListPool.poll();
if (builder == null) {
return new IntListBuilder();
}
builder.clear();
return builder;
}
/**
* Returns an int list builder to the pool.
*/
public void returnIntList(IntListBuilder builder) {
if (builder != null && intListPool.size() < maxPoolSize) {
builder.clear();
intListPool.offer(builder);
}
}
/**
* Borrows a double list builder from the pool.
*/
public DoubleListBuilder borrowDoubleList() {
DoubleListBuilder builder = doubleListPool.poll();
if (builder == null) {
return new DoubleListBuilder();
}
builder.clear();
return builder;
}
/**
* Returns a double list builder to the pool.
*/
public void returnDoubleList(DoubleListBuilder builder) {
if (builder != null && doubleListPool.size() < maxPoolSize) {
builder.clear();
doubleListPool.offer(builder);
}
}
/**
* Gets pool statistics.
*/
public PoolStatistics getStatistics() {
return new PoolStatistics(
arrayPoolHits.get(),
arrayPoolMisses.get(),
arrayPool.size(),
signalBuilderPool.size(),
intListPool.size(),
doubleListPool.size()
);
}
/**
* Holder for reusable arrays.
*/
public static final class ArrayHolder implements AutoCloseable {
public double[] array;
private FinancialAnalysisObjectPool pool;
ArrayHolder(int size) {
this.array = new double[size];
}
void clear() {
// Arrays.fill(array, 0.0); // Optional - only if needed for correctness
}
void setPool(FinancialAnalysisObjectPool pool) {
this.pool = pool;
}
@Override
public void close() {
if (pool != null) {
pool.returnArray(this);
}
}
}
/**
* Efficient builder for trading signals using primitive lists.
*/
public static final class TradingSignalBuilder {
private IntListBuilder indices = new IntListBuilder();
private IntListBuilder types = new IntListBuilder();
private DoubleListBuilder confidences = new DoubleListBuilder();
private java.util.List<String> reasons = new java.util.ArrayList<>();
public void addSignal(int index, FinancialWaveletAnalyzer.SignalType type,
double confidence, String reason) {
indices.add(index);
types.add(type.ordinal());
confidences.add(confidence);
reasons.add(reason);
}
public int size() {
return indices.size();
}
public java.util.List<FinancialWaveletAnalyzer.TradingSignal> build() {
java.util.List<FinancialWaveletAnalyzer.TradingSignal> signals =
new java.util.ArrayList<>(size());
for (int i = 0; i < size(); i++) {
signals.add(new FinancialWaveletAnalyzer.TradingSignal(
indices.get(i),
FinancialWaveletAnalyzer.SignalType.values()[types.get(i)],
confidences.get(i),
reasons.get(i)
));
}
return signals;
}
void clear() {
indices.clear();
types.clear();
confidences.clear();
reasons.clear();
}
}
/**
* Primitive list for int values to avoid boxing.
*/
public static final class IntListBuilder {
private int[] data;
private int size;
IntListBuilder() {
this(16);
}
IntListBuilder(int capacity) {
this.data = new int[capacity];
this.size = 0;
}
public void add(int value) {
ensureCapacity(size + 1);
data[size++] = value;
}
public int get(int index) {
if (index >= size) {
throw new IndexOutOfBoundsException();
}
return data[index];
}
public int size() {
return size;
}
public int[] toArray() {
return java.util.Arrays.copyOf(data, size);
}
public java.util.List<Integer> toList() {
java.util.List<Integer> list = new java.util.ArrayList<>(size);
for (int i = 0; i < size; i++) {
list.add(data[i]);
}
return list;
}
void clear() {
size = 0;
}
private void ensureCapacity(int minCapacity) {
if (minCapacity > data.length) {
int newCapacity = Math.max(data.length * 2, minCapacity);
data = java.util.Arrays.copyOf(data, newCapacity);
}
}
}
/**
* Primitive list for double values to avoid boxing.
*/
public static final class DoubleListBuilder {
private double[] data;
private int size;
DoubleListBuilder() {
this(16);
}
DoubleListBuilder(int capacity) {
this.data = new double[capacity];
this.size = 0;
}
public void add(double value) {
ensureCapacity(size + 1);
data[size++] = value;
}
public double get(int index) {
if (index >= size) {
throw new IndexOutOfBoundsException();
}
return data[index];
}
public int size() {
return size;
}
public double[] toArray() {
return java.util.Arrays.copyOf(data, size);
}
void clear() {
size = 0;
}
private void ensureCapacity(int minCapacity) {
if (minCapacity > data.length) {
int newCapacity = Math.max(data.length * 2, minCapacity);
data = java.util.Arrays.copyOf(data, newCapacity);
}
}
}
/**
* Pool statistics for monitoring.
*
* @param arrayHits number of array allocations served from pool
* @param arrayMisses number of array allocations requiring new allocation
* @param arrayPoolSize current array pool size
* @param signalPoolSize current signal pool size
* @param intListPoolSize current int list pool size
* @param doubleListPoolSize current double list pool size
*/
public record PoolStatistics(
int arrayHits,
int arrayMisses,
int arrayPoolSize,
int signalPoolSize,
int intListPoolSize,
int doubleListPoolSize
) {
public double hitRate() {
int total = arrayHits + arrayMisses;
return total == 0 ? 0.0 : (double) arrayHits / total;
}
}
}