ArrayPool.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. #if !UNITY_EDITOR
  2. // Extra optimizations when not running in the editor, but less error checking
  3. #define ASTAR_OPTIMIZE_POOLING
  4. #endif
  5. using System;
  6. using System.Collections.Generic;
  7. namespace PF {
  8. /** Lightweight Array Pool.
  9. * Handy class for pooling arrays of type T.
  10. *
  11. * Usage:
  12. * - Claim a new array using \code SomeClass[] foo = ArrayPool<SomeClass>.Claim (capacity); \endcode
  13. * - Use it and do stuff with it
  14. * - Release it with \code ArrayPool<SomeClass>.Release (foo); \endcode
  15. *
  16. * \warning Arrays returned from the Claim method may contain arbitrary data.
  17. * You cannot rely on it being zeroed out.
  18. *
  19. * After you have released a array, you should never use it again, if you do use it
  20. * your code may modify it at the same time as some other code is using it which
  21. * will likely lead to bad results.
  22. *
  23. * \since Version 3.8.6
  24. * \see Pathfinding.Util.ListPool
  25. */
  26. public static class ArrayPool<T>{
  27. #if !ASTAR_NO_POOLING
  28. /** Maximum length of an array pooled using ClaimWithExactLength.
  29. * Arrays with lengths longer than this will silently not be pooled.
  30. */
  31. const int MaximumExactArrayLength = 256;
  32. /** Internal pool.
  33. * The arrays in each bucket have lengths of 2^i
  34. */
  35. static readonly Stack<T[]>[] pool = new Stack<T[]>[31];
  36. static readonly Stack<T[]>[] exactPool = new Stack<T[]>[MaximumExactArrayLength+1];
  37. static readonly HashSet<T[]> inPool = new HashSet<T[]>();
  38. #endif
  39. /** Returns an array with at least the specified length */
  40. public static T[] Claim (int minimumLength) {
  41. if (minimumLength <= 0) {
  42. return ClaimWithExactLength(0);
  43. }
  44. int bucketIndex = 0;
  45. while ((1 << bucketIndex) < minimumLength && bucketIndex < 30) {
  46. bucketIndex++;
  47. }
  48. if (bucketIndex == 30)
  49. throw new System.ArgumentException("Too high minimum length");
  50. #if !ASTAR_NO_POOLING
  51. lock (pool) {
  52. if (pool[bucketIndex] == null) {
  53. pool[bucketIndex] = new Stack<T[]>();
  54. }
  55. if (pool[bucketIndex].Count > 0) {
  56. var array = pool[bucketIndex].Pop();
  57. inPool.Remove(array);
  58. return array;
  59. }
  60. }
  61. #endif
  62. return new T[1 << bucketIndex];
  63. }
  64. /** Returns an array with the specified length.
  65. * Use with caution as pooling too many arrays with different lengths that
  66. * are rarely being reused will lead to an effective memory leak.
  67. *
  68. * Use #Claim if you just need an array that is at least as large as some value.
  69. */
  70. public static T[] ClaimWithExactLength (int length) {
  71. #if !ASTAR_NO_POOLING
  72. bool isPowerOfTwo = length != 0 && (length & (length - 1)) == 0;
  73. if (isPowerOfTwo) {
  74. // Will return the correct array length
  75. return Claim(length);
  76. }
  77. if (length <= MaximumExactArrayLength) {
  78. lock (pool) {
  79. Stack<T[]> stack = exactPool[length];
  80. if (stack != null && stack.Count > 0) {
  81. var array = stack.Pop();
  82. #if !ASTAR_OPTIMIZE_POOLING
  83. inPool.Remove(array);
  84. #endif
  85. return array;
  86. }
  87. }
  88. }
  89. #endif
  90. return new T[length];
  91. }
  92. /** Pool an array.
  93. * If the array was got using the #ClaimWithExactLength method then the \a allowNonPowerOfTwo parameter must be set to true.
  94. * The parameter exists to make sure that non power of two arrays are not pooled unintentionally which could lead to memory leaks.
  95. */
  96. public static void Release (ref T[] array, bool allowNonPowerOfTwo = false) {
  97. if (array == null) return;
  98. if (array.GetType() != typeof(T[])) {
  99. throw new System.ArgumentException("Expected array type " + typeof(T[]).Name + " but found " + array.GetType().Name + "\nAre you using the correct generic class?\n");
  100. }
  101. #if !ASTAR_NO_POOLING
  102. bool isPowerOfTwo = array.Length != 0 && (array.Length & (array.Length - 1)) == 0;
  103. if (!isPowerOfTwo && !allowNonPowerOfTwo && array.Length != 0) throw new System.ArgumentException("Length is not a power of 2");
  104. lock (pool) {
  105. #if !ASTAR_OPTIMIZE_POOLING
  106. if (!inPool.Add(array)) {
  107. throw new InvalidOperationException("You are trying to pool an array twice. Please make sure that you only pool it once.");
  108. }
  109. #endif
  110. if (isPowerOfTwo) {
  111. int bucketIndex = 0;
  112. while ((1 << bucketIndex) < array.Length && bucketIndex < 30) {
  113. bucketIndex++;
  114. }
  115. if (pool[bucketIndex] == null) {
  116. pool[bucketIndex] = new Stack<T[]>();
  117. }
  118. pool[bucketIndex].Push(array);
  119. } else if (array.Length <= MaximumExactArrayLength) {
  120. Stack<T[]> stack = exactPool[array.Length];
  121. if (stack == null) stack = exactPool[array.Length] = new Stack<T[]>();
  122. stack.Push(array);
  123. }
  124. }
  125. #endif
  126. array = null;
  127. }
  128. }
  129. /** Extension methods for List<T> */
  130. public static class ListExtensions {
  131. /** Identical to ToArray but it uses ArrayPool<T> to avoid allocations if possible.
  132. *
  133. * Use with caution as pooling too many arrays with different lengths that
  134. * are rarely being reused will lead to an effective memory leak.
  135. */
  136. public static T[] ToArrayFromPool<T>(this List<T> list) {
  137. var arr = ArrayPool<T>.ClaimWithExactLength(list.Count);
  138. for (int i = 0; i < arr.Length; i++) {
  139. arr[i] = list[i];
  140. }
  141. return arr;
  142. }
  143. /** Clear a list faster than List<T>.Clear.
  144. * It turns out that the List<T>.Clear method will clear all elements in the underlaying array
  145. * not just the ones up to Count. If the list only has a few elements, but the capacity
  146. * is huge, this can cause performance problems. Using the RemoveRange method to remove
  147. * all elements in the list does not have this problem, however it is implemented in a
  148. * stupid way, so it will clear the elements twice (completely unnecessarily) so it will
  149. * only be faster than using the Clear method if the number of elements in the list is
  150. * less than half of the capacity of the list.
  151. *
  152. * Hopefully this method can be removed when Unity upgrades to a newer version of Mono.
  153. */
  154. public static void ClearFast<T>(this List<T> list) {
  155. if (list.Count*2 < list.Capacity) {
  156. list.RemoveRange(0, list.Count);
  157. } else {
  158. list.Clear();
  159. }
  160. }
  161. }
  162. }