internal void BalanceRAM()
{
// We flush when we've used our target usage
long flushTrigger = ramBufferSize;
long deletesRAMUsed = deletesInRAM.bytesUsed + deletesFlushed.bytesUsed;
if (numBytesAlloc + deletesRAMUsed > freeTrigger)
{
if (infoStream != null)
Message(
" RAM: now balance allocations: usedMB=" + ToMB(numBytesUsed) +
" vs trigger=" + ToMB(flushTrigger) +
" allocMB=" + ToMB(numBytesAlloc) +
" deletesMB=" + ToMB(deletesRAMUsed) +
" vs trigger=" + ToMB(freeTrigger) +
" byteBlockFree=" + ToMB(byteBlockAllocator.freeByteBlocks.Count * BYTE_BLOCK_SIZE) +
" perDocFree=" + ToMB(perDocAllocator.freeByteBlocks.Count * PER_DOC_BLOCK_SIZE) +
" charBlockFree=" + ToMB(freeCharBlocks.Count * CHAR_BLOCK_SIZE * CHAR_NUM_BYTE));
long startBytesAlloc = numBytesAlloc + deletesRAMUsed;
int iter = 0;
// We free equally from each pool in 32 KB
// chunks until we are below our threshold
// (freeLevel)
bool any = true;
while (numBytesAlloc + deletesRAMUsed > freeLevel)
{
lock (this)
{
if (0 == perDocAllocator.freeByteBlocks.Count
&& 0 == byteBlockAllocator.freeByteBlocks.Count
&& 0 == freeCharBlocks.Count
&& 0 == freeIntBlocks.Count
&& !any)
{
// Nothing else to free -- must flush now.
bufferIsFull = numBytesUsed + deletesRAMUsed > flushTrigger;
if (infoStream != null)
{
if (bufferIsFull)
Message(" nothing to free; now set bufferIsFull");
else
Message(" nothing to free");
}
System.Diagnostics.Debug.Assert(numBytesUsed <= numBytesAlloc);
break;
}
if ((0 == iter % 5) && byteBlockAllocator.freeByteBlocks.Count > 0)
{
byteBlockAllocator.freeByteBlocks.RemoveAt(byteBlockAllocator.freeByteBlocks.Count - 1);
numBytesAlloc -= BYTE_BLOCK_SIZE;
}
if ((1 == iter % 5) && freeCharBlocks.Count > 0)
{
freeCharBlocks.RemoveAt(freeCharBlocks.Count - 1);
numBytesAlloc -= CHAR_BLOCK_SIZE * CHAR_NUM_BYTE;
}
if ((2 == iter % 5) && freeIntBlocks.Count > 0)
{
freeIntBlocks.RemoveAt(freeIntBlocks.Count - 1);
numBytesAlloc -= INT_BLOCK_SIZE * INT_NUM_BYTE;
}
if ((3 == iter % 5) && perDocAllocator.freeByteBlocks.Count > 0)
{
// Remove upwards of 32 blocks (each block is 1K)
for (int i = 0; i < 32; ++i)
{
perDocAllocator.freeByteBlocks.RemoveAt(perDocAllocator.freeByteBlocks.Count - 1);
numBytesAlloc -= PER_DOC_BLOCK_SIZE;
if (perDocAllocator.freeByteBlocks.Count == 0)
{
break;
}
}
}
}
if ((4 == iter % 5) && any)
// Ask consumer to free any recycled state
any = consumer.FreeRAM();
iter++;
}
if (infoStream != null)
Message(System.String.Format(nf, " after free: freedMB={0:f} usedMB={1:f} allocMB={2:f}",
new System.Object[] { ((startBytesAlloc - numBytesAlloc) / 1024.0 / 1024.0), (numBytesUsed / 1024.0 / 1024.0), (numBytesAlloc / 1024.0 / 1024.0) }));
}
else
{
// If we have not crossed the 100% mark, but have
// crossed the 95% mark of RAM we are actually
// using, go ahead and flush. This prevents
// over-allocating and then freeing, with every
// flush.
lock (this)
{
if (numBytesUsed + deletesRAMUsed > flushTrigger)
{
if (infoStream != null)
Message(System.String.Format(nf, " RAM: now flush @ usedMB={0:f} allocMB={1:f} triggerMB={2:f}",
new object[] { (numBytesUsed / 1024.0 / 1024.0), (numBytesAlloc / 1024.0 / 1024.0), (flushTrigger / 1024.0 / 1024.0) }));
bufferIsFull = true;
}
}
}
}