PageRenderTime 46ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/Squared/RenderLib/Pools.cs

http://github.com/kevingadd/Fracture
C# | 350 lines | 274 code | 75 blank | 1 comment | 40 complexity | dcb55936ea38e256d7ea33adb2c6978c MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using Squared.Threading;
  6. using Squared.Util;
  7. namespace Squared.Render {
  8. public interface IBatchPool {
  9. void Release (Batch batch);
  10. void SetCapacity (int newCapacity);
  11. }
  12. public class BatchPool<T> : BaseObjectPool<T>, IBatchPool
  13. where T : Batch, new() {
  14. public readonly Func<IBatchPool, T> Allocator;
  15. public BatchPool (Func<IBatchPool, T> allocator, int poolCapacity = BaseObjectPool<T>.DefaultCapacity)
  16. : this(poolCapacity) {
  17. Allocator = allocator;
  18. }
  19. public BatchPool (int poolCapacity)
  20. : base(poolCapacity) {
  21. }
  22. protected override T AllocateNew () {
  23. return Allocator(this);
  24. }
  25. void IBatchPool.Release (Batch batch) {
  26. if (batch.State.IsPrepareQueued)
  27. throw new Exception("Queued for prepare");
  28. Release((T)batch);
  29. }
  30. public void SetCapacity (int newCapacity) {
  31. PoolCapacity = newCapacity;
  32. }
  33. }
  34. public abstract class BaseObjectPool<T>
  35. where T : class {
  36. private UnorderedList<T> _Pool;
  37. public const int DefaultCapacity = 512;
  38. public int PoolCapacity;
  39. public BaseObjectPool ()
  40. : this(DefaultCapacity) {
  41. }
  42. public BaseObjectPool (int poolCapacity) {
  43. PoolCapacity = poolCapacity;
  44. _Pool = new UnorderedList<T>(Math.Max(poolCapacity / 2, 64));
  45. }
  46. public virtual T Allocate () {
  47. T result = null;
  48. lock (_Pool)
  49. _Pool.TryPopFront(out result);
  50. if (result == null)
  51. result = AllocateNew();
  52. return result;
  53. }
  54. protected abstract T AllocateNew ();
  55. public virtual void Release (T obj) {
  56. lock (_Pool) {
  57. if (_Pool.Count > PoolCapacity)
  58. return;
  59. _Pool.Add(obj);
  60. }
  61. }
  62. }
  63. public interface IDrainable {
  64. void WaitForWorkItems ();
  65. }
  66. public class ListPool<T> : IDrainable {
  67. private struct ListClearWorkItem : IWorkItem {
  68. public UnorderedList<T> List;
  69. public UnorderedList<UnorderedList<T>> Pool;
  70. public int Limit;
  71. public void Execute () {
  72. lock (Pool)
  73. if (Pool.Count >= Limit)
  74. return;
  75. List.Clear();
  76. lock (Pool) {
  77. if (Pool.Count >= Limit)
  78. return;
  79. Pool.Add(List);
  80. }
  81. }
  82. }
  83. private UnorderedList<UnorderedList<T>> _Pool = new UnorderedList<UnorderedList<T>>();
  84. private UnorderedList<UnorderedList<T>> _LargePool = new UnorderedList<UnorderedList<T>>();
  85. public int SmallPoolCapacity;
  86. public int LargePoolCapacity;
  87. public readonly int InitialItemSize;
  88. public int SmallPoolMaxItemSize;
  89. public int LargePoolMaxItemSize;
  90. public ThreadGroup ThreadGroup { get; set; }
  91. public ListPool (int smallPoolCapacity, int initialItemSize, int maxItemSize) {
  92. SmallPoolCapacity = smallPoolCapacity;
  93. InitialItemSize = initialItemSize;
  94. SmallPoolMaxItemSize = maxItemSize;
  95. LargePoolCapacity = 0;
  96. LargePoolMaxItemSize = 0;
  97. }
  98. public ListPool (int smallPoolCapacity, int largePoolCapacity, int initialItemSize, int maxSmallItemSize, int maxLargeItemSize) {
  99. SmallPoolCapacity = smallPoolCapacity;
  100. LargePoolCapacity = largePoolCapacity;
  101. InitialItemSize = initialItemSize;
  102. SmallPoolMaxItemSize = maxSmallItemSize;
  103. LargePoolMaxItemSize = maxLargeItemSize;
  104. }
  105. public UnorderedList<T> Allocate (int? capacity, bool capacityIsHint = false) {
  106. UnorderedList<T> result = null;
  107. var isBig = capacity.HasValue &&
  108. (capacity.Value > SmallPoolMaxItemSize);
  109. if (
  110. (LargePoolCapacity > 0) && isBig
  111. ) {
  112. lock (_LargePool)
  113. _LargePool.TryPopFront(out result);
  114. }
  115. if (capacityIsHint || !isBig || (LargePoolCapacity <= 0)) {
  116. lock (_Pool)
  117. _Pool.TryPopFront(out result);
  118. if (
  119. (LargePoolCapacity > 0) &&
  120. (result == null)
  121. ) {
  122. lock (_LargePool)
  123. _LargePool.TryPopFront(out result);
  124. }
  125. }
  126. if (result == null)
  127. result = new UnorderedList<T>(capacity.GetValueOrDefault(InitialItemSize));
  128. return result;
  129. }
  130. void IDrainable.WaitForWorkItems () {
  131. if (ThreadGroup == null)
  132. return;
  133. ThreadGroup.GetQueueForType<ListClearWorkItem>().WaitUntilDrained();
  134. }
  135. private void ClearAndReturn (UnorderedList<T> list, UnorderedList<UnorderedList<T>> pool, int limit) {
  136. if (ThreadGroup != null) {
  137. ThreadGroup.Enqueue(new ListClearWorkItem {
  138. List = list,
  139. Pool = pool,
  140. Limit = limit
  141. });
  142. return;
  143. }
  144. if (pool.Count >= limit)
  145. return;
  146. list.Clear();
  147. pool.Add(list);
  148. }
  149. public void Release (ref UnorderedList<T> _list) {
  150. var list = _list;
  151. _list = null;
  152. if (list == null)
  153. return;
  154. if (list.Capacity > SmallPoolMaxItemSize) {
  155. if (list.Capacity < LargePoolMaxItemSize) {
  156. lock (_LargePool) {
  157. if (_LargePool.Count >= LargePoolCapacity)
  158. return;
  159. }
  160. lock (_LargePool)
  161. ClearAndReturn(list, _LargePool, LargePoolCapacity);
  162. }
  163. return;
  164. }
  165. lock (_Pool) {
  166. if (_Pool.Count >= SmallPoolCapacity)
  167. return;
  168. }
  169. lock (_Pool)
  170. ClearAndReturn(list, _Pool, SmallPoolCapacity);
  171. }
  172. }
  173. public interface IPoolAllocator {
  174. void Collect ();
  175. }
  176. public interface IArrayPoolAllocator {
  177. void Step ();
  178. }
  179. // Thread-safe
  180. public class ArrayPoolAllocator<T> : IArrayPoolAllocator {
  181. public struct Allocation {
  182. public readonly int Origin;
  183. public readonly T[] Buffer;
  184. internal Allocation (int origin, T[] buffer) {
  185. Origin = origin;
  186. Buffer = buffer;
  187. }
  188. }
  189. public class Pool : UnorderedList<T[]> {
  190. public UnorderedList<Allocation> LiveAllocations = new UnorderedList<Allocation>(64);
  191. public readonly int AllocationSize;
  192. public Pool (int allocationSize, int capacity)
  193. : base(capacity) {
  194. AllocationSize = allocationSize;
  195. }
  196. }
  197. public const int MinPower = 6;
  198. public const int MaxPower = 20;
  199. public const int PowerCount = (MaxPower - MinPower) + 1;
  200. public const int MinSize = 1 << MinPower;
  201. public const int MaxSize = 1 << MaxPower;
  202. public const int CollectionAge = 2;
  203. public const int DefaultPoolCapacity = 32;
  204. public const int MaxPoolCapacity = 512;
  205. private int StepIndex = 0;
  206. private Pool[] Pools = new Pool[PowerCount];
  207. public ArrayPoolAllocator () {
  208. for (int power = MinPower; power <= MaxPower; power++)
  209. Pools[power - MinPower] = new Pool(1 << power, DefaultPoolCapacity);
  210. }
  211. private static int IntLog2 (int x) {
  212. int l = 0;
  213. if (x >= 1 << 16) {
  214. x >>= 16;
  215. l |= 16;
  216. }
  217. if (x >= 1 << 8) {
  218. x >>= 8;
  219. l |= 8;
  220. }
  221. if (x >= 1 << 4) {
  222. x >>= 4;
  223. l |= 4;
  224. }
  225. if (x >= 1 << 2) {
  226. x >>= 2;
  227. l |= 2;
  228. }
  229. if (x >= 1 << 1)
  230. l |= 1;
  231. return l;
  232. }
  233. private int SelectPool (int capacity) {
  234. int log2 = IntLog2(capacity) - MinPower + 1;
  235. if (log2 < 0)
  236. log2 = 0;
  237. for (int power = log2; power < PowerCount; power++) {
  238. var poolSize = Pools[power].AllocationSize;
  239. if (poolSize > capacity)
  240. return power;
  241. }
  242. throw new InvalidOperationException("Allocation size out of range");
  243. }
  244. public Allocation Allocate (int capacity) {
  245. int poolId = SelectPool(capacity);
  246. var pool = Pools[poolId];
  247. T[] result;
  248. lock (pool)
  249. pool.TryPopFront(out result);
  250. if (result == null)
  251. result = new T[pool.AllocationSize];
  252. var a = new Allocation(StepIndex, result);
  253. lock (pool)
  254. pool.LiveAllocations.Add(a);
  255. return a;
  256. }
  257. public void Step () {
  258. int expirationThreshold = StepIndex - CollectionAge;
  259. StepIndex += 1;
  260. for (int power = 0; power < PowerCount; power++) {
  261. var pool = Pools[power];
  262. lock (pool) {
  263. using (var e = pool.LiveAllocations.GetEnumerator())
  264. while (e.MoveNext()) {
  265. var a = e.Current;
  266. if (a.Origin <= expirationThreshold) {
  267. pool.Add(a.Buffer);
  268. e.RemoveCurrent();
  269. }
  270. }
  271. }
  272. }
  273. }
  274. }
  275. }