package uk.ac.imperial.lsds.seepworker.core; import static com.codahale.metrics.MetricRegistry.name; import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.Deque; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.codahale.metrics.Counter; import uk.ac.imperial.lsds.seep.metrics.SeepMetrics; import uk.ac.imperial.lsds.seepworker.WorkerConfig; public class BufferPool { final private Logger LOG = LoggerFactory.getLogger(BufferPool.class.getName()); private final int minBufferSize; private final long totalMemAvailableToBufferPool; private Deque<ByteBuffer> allocatedBuffers; // Metrics // usedMemory is allocated memory that is currently used by some Dataset // Note that the total memory usage may be higher, as buffers are pooled and not // released immediately. However, effectively, the total memory available would be // the total_memory_available - usedMemory final private Counter usedMemory; //keep track of all memory which was allocated for better bounds checking. final private Counter allocatedMemory; public static BufferPool __TEMPORAL_FAKE() { return new BufferPool(-666); } private BufferPool(int a) { this.minBufferSize = 8192; this.totalMemAvailableToBufferPool = (int)(Runtime.getRuntime().totalMemory())/2; this.allocatedBuffers = new ArrayDeque<ByteBuffer>(); LOG.warn("TEMPORAL-> dangling buffer pools"); usedMemory = SeepMetrics.REG.counter(name(BufferPool.class, "total", "mem")); allocatedMemory = SeepMetrics.REG.counter(name(BufferPool.class, "allocated", "mem")); preAllocatePoolOfBuffers(); } private BufferPool(WorkerConfig wc) { this.minBufferSize = wc.getInt(WorkerConfig.BUFFERPOOL_MIN_BUFFER_SIZE); this.totalMemAvailableToBufferPool = wc.getLong(WorkerConfig.BUFFERPOOL_MAX_MEM_AVAILABLE); this.allocatedBuffers = new ArrayDeque<ByteBuffer>(); usedMemory = SeepMetrics.REG.counter(name(BufferPool.class, "event", "mem")); allocatedMemory = SeepMetrics.REG.counter(name(BufferPool.class, "allocated", "mem")); preAllocatePoolOfBuffers(); LOG.info("Created new Buffer Pool with availableMemory of {} and minBufferSize of: {}", this.totalMemAvailableToBufferPool, this.minBufferSize); } public static BufferPool createBufferPool(WorkerConfig wc) { return new BufferPool(wc); } public int getMinimumBufferSize() { return this.minBufferSize; } /** * Will return a ByteBuffer from the pool if available. If not, it will try to allocate a new ByteBuffer, * an operation that will succeed if there is enough memory available. If there is not enough memory * available, the method returns null * @return */ public synchronized ByteBuffer borrowBuffer() { if(allocatedBuffers.size() > 0) { ByteBuffer bb = allocatedBuffers.pop(); bb.clear(); //allocatedMemory.inc(minBufferSize); usedMemory.inc(minBufferSize); return bb; } else { if(enoughMemoryAvailable()){ allocatedMemory.inc(minBufferSize); usedMemory.inc(minBufferSize); return allocateByteBuffer(); } else { return null; } } } private ByteBuffer allocateByteBuffer() { return ByteBuffer.allocate(minBufferSize); } public ByteBuffer getCacheBuffer() { ByteBuffer bb = allocateByteBuffer(); return bb; } public int returnBuffer(ByteBuffer buffer) { int freedMemory = buffer.capacity(); usedMemory.dec(minBufferSize); allocatedBuffers.add(buffer); return freedMemory; } private boolean enoughMemoryAvailable() { // Any headroom should have been incorporated on bufferPool creation (e.g. aprox. constant mem usage on steady state) if(allocatedMemory.getCount() + minBufferSize > totalMemAvailableToBufferPool) { return false; } return true; } public boolean isThereXMemAvailable(long size) { long availableMemory = totalMemAvailableToBufferPool - usedMemory.getCount(); if(size < availableMemory) { return true; } return false; } public double getPercAvailableMemory() { double availableMemory = totalMemAvailableToBufferPool - usedMemory.getCount(); return availableMemory/(double)totalMemAvailableToBufferPool; } private void preAllocatePoolOfBuffers() { LOG.info("Creating buffer pool... Pooling buffers"); while(allocatedMemory.getCount() + this.minBufferSize < this.totalMemAvailableToBufferPool) { usedMemory.inc(minBufferSize); allocatedMemory.inc(minBufferSize); ByteBuffer n = this.allocateByteBuffer(); this.returnBuffer(n); } LOG.info("Pooling buffers... {} buffers created", this.allocatedBuffers.size()); } }