ArrayPool.cs 6.5 KB

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