| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571 |
- //----------------------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //----------------------------------------------------------------------------
- namespace System.Runtime
- {
- using System;
- using System.Collections.Generic;
- using System.Threading;
- #if DEBUG
- using System.Collections.Concurrent;
- using System.Diagnostics;
- using System.Globalization;
- using System.Security;
- using System.Security.Permissions;
- #endif //DEBUG
- abstract class InternalBufferManager
- {
- protected InternalBufferManager()
- {
- }
- public abstract byte[] TakeBuffer(int bufferSize);
- public abstract void ReturnBuffer(byte[] buffer);
- public abstract void Clear();
- public static InternalBufferManager Create(long maxBufferPoolSize, int maxBufferSize)
- {
- if (maxBufferPoolSize == 0)
- {
- return GCBufferManager.Value;
- }
- else
- {
- Fx.Assert(maxBufferPoolSize > 0 && maxBufferSize >= 0, "bad params, caller should verify");
- return new PooledBufferManager(maxBufferPoolSize, maxBufferSize);
- }
- }
- class PooledBufferManager : InternalBufferManager
- {
- const int minBufferSize = 128;
- const int maxMissesBeforeTuning = 8;
- const int initialBufferCount = 1;
- readonly object tuningLock;
- int[] bufferSizes;
- BufferPool[] bufferPools;
- long memoryLimit;
- long remainingMemory;
- bool areQuotasBeingTuned;
- int totalMisses;
- #if DEBUG
- ConcurrentDictionary<int, string> buffersPooled = new ConcurrentDictionary<int, string>();
- #endif //DEBUG
- public PooledBufferManager(long maxMemoryToPool, int maxBufferSize)
- {
- this.tuningLock = new object();
- this.memoryLimit = maxMemoryToPool;
- this.remainingMemory = maxMemoryToPool;
- List<BufferPool> bufferPoolList = new List<BufferPool>();
- for (int bufferSize = minBufferSize;;)
- {
- long bufferCountLong = this.remainingMemory / bufferSize;
- int bufferCount = bufferCountLong > int.MaxValue ? int.MaxValue : (int)bufferCountLong;
- if (bufferCount > initialBufferCount)
- {
- bufferCount = initialBufferCount;
- }
- bufferPoolList.Add(BufferPool.CreatePool(bufferSize, bufferCount));
- this.remainingMemory -= (long)bufferCount * bufferSize;
- if (bufferSize >= maxBufferSize)
- {
- break;
- }
- long newBufferSizeLong = (long)bufferSize * 2;
- if (newBufferSizeLong > (long)maxBufferSize)
- {
- bufferSize = maxBufferSize;
- }
- else
- {
- bufferSize = (int)newBufferSizeLong;
- }
- }
- this.bufferPools = bufferPoolList.ToArray();
- this.bufferSizes = new int[bufferPools.Length];
- for (int i = 0; i < bufferPools.Length; i++)
- {
- this.bufferSizes[i] = bufferPools[i].BufferSize;
- }
- }
- public override void Clear()
- {
- #if DEBUG
- this.buffersPooled.Clear();
- #endif //DEBUG
- for (int i = 0; i < this.bufferPools.Length; i++)
- {
- BufferPool bufferPool = this.bufferPools[i];
- bufferPool.Clear();
- }
- }
- void ChangeQuota(ref BufferPool bufferPool, int delta)
- {
- if (TraceCore.BufferPoolChangeQuotaIsEnabled(Fx.Trace))
- {
- TraceCore.BufferPoolChangeQuota(Fx.Trace, bufferPool.BufferSize, delta);
- }
- BufferPool oldBufferPool = bufferPool;
- int newLimit = oldBufferPool.Limit + delta;
- BufferPool newBufferPool = BufferPool.CreatePool(oldBufferPool.BufferSize, newLimit);
- for (int i = 0; i < newLimit; i++)
- {
- byte[] buffer = oldBufferPool.Take();
- if (buffer == null)
- {
- break;
- }
- newBufferPool.Return(buffer);
- newBufferPool.IncrementCount();
- }
- this.remainingMemory -= oldBufferPool.BufferSize * delta;
- bufferPool = newBufferPool;
- }
- void DecreaseQuota(ref BufferPool bufferPool)
- {
- ChangeQuota(ref bufferPool, -1);
- }
- int FindMostExcessivePool()
- {
- long maxBytesInExcess = 0;
- int index = -1;
- for (int i = 0; i < this.bufferPools.Length; i++)
- {
- BufferPool bufferPool = this.bufferPools[i];
- if (bufferPool.Peak < bufferPool.Limit)
- {
- long bytesInExcess = (bufferPool.Limit - bufferPool.Peak) * (long)bufferPool.BufferSize;
- if (bytesInExcess > maxBytesInExcess)
- {
- index = i;
- maxBytesInExcess = bytesInExcess;
- }
- }
- }
- return index;
- }
- int FindMostStarvedPool()
- {
- long maxBytesMissed = 0;
- int index = -1;
- for (int i = 0; i < this.bufferPools.Length; i++)
- {
- BufferPool bufferPool = this.bufferPools[i];
- if (bufferPool.Peak == bufferPool.Limit)
- {
- long bytesMissed = bufferPool.Misses * (long)bufferPool.BufferSize;
- if (bytesMissed > maxBytesMissed)
- {
- index = i;
- maxBytesMissed = bytesMissed;
- }
- }
- }
- return index;
- }
- BufferPool FindPool(int desiredBufferSize)
- {
- for (int i = 0; i < this.bufferSizes.Length; i++)
- {
- if (desiredBufferSize <= this.bufferSizes[i])
- {
- return this.bufferPools[i];
- }
- }
- return null;
- }
- void IncreaseQuota(ref BufferPool bufferPool)
- {
- ChangeQuota(ref bufferPool, 1);
- }
- public override void ReturnBuffer(byte[] buffer)
- {
- Fx.Assert(buffer != null, "caller must verify");
- #if DEBUG
- int hash = buffer.GetHashCode();
- if (!this.buffersPooled.TryAdd(hash, CaptureStackTrace()))
- {
- string originalStack;
- if (!this.buffersPooled.TryGetValue(hash, out originalStack))
- {
- originalStack = "NULL";
- }
- Fx.Assert(
- string.Format(
- CultureInfo.InvariantCulture,
- "Buffer '{0}' has already been returned to the bufferManager before. Previous CallStack: {1} Current CallStack: {2}",
- hash,
- originalStack,
- CaptureStackTrace()));
- }
- #endif //DEBUG
- BufferPool bufferPool = FindPool(buffer.Length);
- if (bufferPool != null)
- {
- if (buffer.Length != bufferPool.BufferSize)
- {
- throw Fx.Exception.Argument("buffer", InternalSR.BufferIsNotRightSizeForBufferManager);
- }
- if (bufferPool.Return(buffer))
- {
- bufferPool.IncrementCount();
- }
- }
- }
- public override byte[] TakeBuffer(int bufferSize)
- {
- Fx.Assert(bufferSize >= 0, "caller must ensure a non-negative argument");
- BufferPool bufferPool = FindPool(bufferSize);
- byte[] returnValue;
- if (bufferPool != null)
- {
- byte[] buffer = bufferPool.Take();
- if (buffer != null)
- {
- bufferPool.DecrementCount();
- returnValue = buffer;
- }
- else
- {
- if (bufferPool.Peak == bufferPool.Limit)
- {
- bufferPool.Misses++;
- if (++totalMisses >= maxMissesBeforeTuning)
- {
- TuneQuotas();
- }
- }
- if (TraceCore.BufferPoolAllocationIsEnabled(Fx.Trace))
- {
- TraceCore.BufferPoolAllocation(Fx.Trace, bufferPool.BufferSize);
- }
- returnValue = Fx.AllocateByteArray(bufferPool.BufferSize);
- }
- }
- else
- {
- if (TraceCore.BufferPoolAllocationIsEnabled(Fx.Trace))
- {
- TraceCore.BufferPoolAllocation(Fx.Trace, bufferSize);
- }
- returnValue = Fx.AllocateByteArray(bufferSize);
- }
- #if DEBUG
- string dummy;
- this.buffersPooled.TryRemove(returnValue.GetHashCode(), out dummy);
- #endif //DEBUG
- return returnValue;
- }
- #if DEBUG
- [SecuritySafeCritical]
- [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
- private static string CaptureStackTrace()
- {
- return new StackTrace(true).ToString();
- }
- #endif //DEBUG
- void TuneQuotas()
- {
- if (this.areQuotasBeingTuned)
- {
- return;
- }
- bool lockHeld = false;
- try
- {
- Monitor.TryEnter(this.tuningLock, ref lockHeld);
- // Don't bother if another thread already has the lock
- if (!lockHeld || this.areQuotasBeingTuned)
- {
- return;
- }
- this.areQuotasBeingTuned = true;
- }
- finally
- {
- if (lockHeld)
- {
- Monitor.Exit(this.tuningLock);
- }
- }
- // find the "poorest" pool
- int starvedIndex = FindMostStarvedPool();
- if (starvedIndex >= 0)
- {
- BufferPool starvedBufferPool = this.bufferPools[starvedIndex];
- if (this.remainingMemory < starvedBufferPool.BufferSize)
- {
- // find the "richest" pool
- int excessiveIndex = FindMostExcessivePool();
- if (excessiveIndex >= 0)
- {
- // steal from the richest
- DecreaseQuota(ref this.bufferPools[excessiveIndex]);
- }
- }
- if (this.remainingMemory >= starvedBufferPool.BufferSize)
- {
- // give to the poorest
- IncreaseQuota(ref this.bufferPools[starvedIndex]);
- }
- }
- // reset statistics
- for (int i = 0; i < this.bufferPools.Length; i++)
- {
- BufferPool bufferPool = this.bufferPools[i];
- bufferPool.Misses = 0;
- }
- this.totalMisses = 0;
- this.areQuotasBeingTuned = false;
- }
- abstract class BufferPool
- {
- int bufferSize;
- int count;
- int limit;
- int misses;
- int peak;
- public BufferPool(int bufferSize, int limit)
- {
- this.bufferSize = bufferSize;
- this.limit = limit;
- }
- public int BufferSize
- {
- get { return this.bufferSize; }
- }
- public int Limit
- {
- get { return this.limit; }
- }
- public int Misses
- {
- get { return this.misses; }
- set { this.misses = value; }
- }
- public int Peak
- {
- get { return this.peak; }
- }
- public void Clear()
- {
- this.OnClear();
- this.count = 0;
- }
- public void DecrementCount()
- {
- int newValue = this.count - 1;
- if (newValue >= 0)
- {
- this.count = newValue;
- }
- }
- public void IncrementCount()
- {
- int newValue = this.count + 1;
- if (newValue <= this.limit)
- {
- this.count = newValue;
- if (newValue > this.peak)
- {
- this.peak = newValue;
- }
- }
- }
- internal abstract byte[] Take();
- internal abstract bool Return(byte[] buffer);
- internal abstract void OnClear();
- internal static BufferPool CreatePool(int bufferSize, int limit)
- {
- // To avoid many buffer drops during training of large objects which
- // get allocated on the LOH, we use the LargeBufferPool and for
- // bufferSize < 85000, the SynchronizedPool. However if bufferSize < 85000
- // and (bufferSize + array-overhead) > 85000, this would still use
- // the SynchronizedPool even though it is allocated on the LOH.
- if (bufferSize < 85000)
- {
- return new SynchronizedBufferPool(bufferSize, limit);
- }
- else
- {
- return new LargeBufferPool(bufferSize, limit);
- }
- }
- class SynchronizedBufferPool : BufferPool
- {
- SynchronizedPool<byte[]> innerPool;
- internal SynchronizedBufferPool(int bufferSize, int limit)
- : base(bufferSize, limit)
- {
- this.innerPool = new SynchronizedPool<byte[]>(limit);
- }
- internal override void OnClear()
- {
- this.innerPool.Clear();
- }
- internal override byte[] Take()
- {
- return this.innerPool.Take();
- }
- internal override bool Return(byte[] buffer)
- {
- return this.innerPool.Return(buffer);
- }
- }
- class LargeBufferPool : BufferPool
- {
- Stack<byte[]> items;
- internal LargeBufferPool(int bufferSize, int limit)
- : base(bufferSize, limit)
- {
- this.items = new Stack<byte[]>(limit);
- }
- object ThisLock
- {
- get
- {
- return this.items;
- }
- }
- internal override void OnClear()
- {
- lock (ThisLock)
- {
- this.items.Clear();
- }
- }
- internal override byte[] Take()
- {
- lock (ThisLock)
- {
- if (this.items.Count > 0)
- {
- return this.items.Pop();
- }
- }
- return null;
- }
- internal override bool Return(byte[] buffer)
- {
- lock (ThisLock)
- {
- if (this.items.Count < this.Limit)
- {
- this.items.Push(buffer);
- return true;
- }
- }
- return false;
- }
- }
- }
- }
- class GCBufferManager : InternalBufferManager
- {
- static GCBufferManager value = new GCBufferManager();
- GCBufferManager()
- {
- }
- public static GCBufferManager Value
- {
- get { return value; }
- }
- public override void Clear()
- {
- }
- public override byte[] TakeBuffer(int bufferSize)
- {
- return Fx.AllocateByteArray(bufferSize);
- }
- public override void ReturnBuffer(byte[] buffer)
- {
- // do nothing, GC will reclaim this buffer
- }
- }
- }
- }
|