暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

BenchmarkContainerParallel.cs 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. using Unity.PerformanceTesting;
  2. using Unity.PerformanceTesting.Benchmark;
  3. using Unity.Burst;
  4. using Unity.Collections.LowLevel.Unsafe;
  5. using Unity.Jobs;
  6. using System.Runtime.InteropServices;
  7. namespace Unity.Collections.PerformanceTests
  8. {
  9. /// <summary>
  10. /// Interface to implement container performance tests which will run using <see cref="BenchmarkContainerRunnerParallel{T}.Run))"/>.
  11. /// Deriving tests from this interface enables both Performance Test Framework and Benchmark Framework to generate and run
  12. /// tests for the contexts described by <see cref="BenchmarkContainerType"/>.
  13. /// </summary>
  14. public interface IBenchmarkContainerParallel
  15. {
  16. /// <summary>
  17. /// Override this to add extra int arguments to a performance test implementation as fields in the implementing type. These arguments
  18. /// are optionally passed in through <see cref="BenchmarkContainerRunner{T}.Run(int, BenchmarkContainerType, int[])"/>.
  19. /// </summary>
  20. /// <param name="capacity">The initial capacity to requested for the container.</param>
  21. /// <param name="args">A variable number of extra arguments to passed through to the test implementation</param>
  22. public void SetParams(int capacity, params int[] args) { }
  23. /// <summary>
  24. /// Called during setup for each measurement in a sample set with the capacity to allocate to the native container
  25. /// when the benchmark type is <see cref="BenchmarkContainerType.Native"/>, <see cref="BenchmarkContainerType.NativeBurstNoSafety"/>,
  26. /// or <see cref="BenchmarkContainerType.NativeBurstSafety"/>.<para />
  27. /// This is also called during teardown for each measurement in a sample set with '-1' to indicate freeing the container.
  28. /// </summary>
  29. /// <param name="capacity">The capacity to allocate for the managed container. Capacity of 0 will still create a container,
  30. /// but it will be empty. A capacity of -1 will dispose the container and free associated allocation(s).</param>
  31. public void AllocNativeContainer(int capacity);
  32. /// <summary>
  33. /// Called during setup for each measurement in a sample set with the capacity to allocate to the unsafe container
  34. /// when the benchmark type is <see cref="BenchmarkContainerType.Unsafe"/>, <see cref="BenchmarkContainerType.UnsafeBurstNoSafety"/>,
  35. /// or <see cref="BenchmarkContainerType.UnsafeBurstSafety"/>.<para />
  36. /// This is also called during teardown for each measurement in a sample set with '-1' to indicate freeing the container.
  37. /// </summary>
  38. /// <param name="capacity">The capacity to allocate for the managed container. Capacity of 0 will still create a container,
  39. /// but it will be empty. A capacity of -1 will dispose the container and free associated allocation(s).</param>
  40. public void AllocUnsafeContainer(int capacity);
  41. /// <summary>
  42. /// Called during setup for each measurement in a sample set with the capacity to allocate to the managed container
  43. /// when the benchmark type is <see cref="BenchmarkContainerConfig.BCL"/>.<para />
  44. /// This is also called during teardown for each measurement in a sample set with '-1' to indicate freeing the container.
  45. /// </summary>
  46. /// <param name="capacity">The capacity to allocate for the managed container. Capacity of 0 will still create a container,
  47. /// but it will be empty. A capacity of -1 will dispose the container and free associated allocation(s).</param>
  48. /// <returns>A reference to the allocated container when capacity &gt;= 0, and `null` when capacity &lt; 0.</returns>
  49. public object AllocBclContainer(int capacity);
  50. /// <summary>
  51. /// The code which will be executed during performance measurement. This should usually be general enough to
  52. /// work with any native container.
  53. /// </summary>
  54. /// <param name="worker">The worker index out of the number of job workers requested for parallel benchmarking</param>
  55. /// <param name="threadIndex">The job system thread index which must be specified in some cases for a container's ParallelWriter</param>
  56. public void MeasureNativeContainer(int worker, int threadIndex);
  57. /// <summary>
  58. /// The code which will be executed during performance measurement. This should usually be general enough to
  59. /// work with any unsafe container.
  60. /// </summary>
  61. /// <param name="worker">The worker index out of the number of job workers requested for parallel benchmarking</param>
  62. /// <param name="threadIndex">The job system thread index which must be specified in some cases for a container's ParallelWriter</param>
  63. public void MeasureUnsafeContainer(int worker, int threadIndex);
  64. /// <summary>
  65. /// The code which will be executed during performance measurement. This should usually be general enough to
  66. /// work with any managed container provided by the Base Class Library (BCL).
  67. /// </summary>
  68. /// <param name="container">A reference to the managed container allocated in <see cref="AllocBclContainer(int)"/></param>
  69. /// <param name="worker">The worker index out of the number of job workers requested for parallel benchmarking</param>
  70. public void MeasureBclContainer(object container, int worker);
  71. }
  72. /// <summary>
  73. /// Provides the API for running container based Performance Framework tests and Benchmark Framework measurements.
  74. /// This will typically be the sole call from a performance test. See <see cref="Run(int, BenchmarkContainerType, int[])"/>
  75. /// for more information.
  76. /// </summary>
  77. /// <typeparam name="T">An implementation conforming to the <see cref="IBenchmarkContainer"/> interface for running container performance tests and benchmarks.</typeparam>
  78. [BurstCompile(CompileSynchronously = true)]
  79. public static class BenchmarkContainerRunnerParallel<T> where T : unmanaged, IBenchmarkContainerParallel
  80. {
  81. [BurstCompile(CompileSynchronously = true, DisableSafetyChecks = true)]
  82. unsafe struct NativeJobBurstST : IJob
  83. {
  84. [NativeDisableUnsafePtrRestriction] public T* methods;
  85. public void Execute() => methods->MeasureNativeContainer(0, 0);
  86. }
  87. [BurstCompile(CompileSynchronously = true, DisableSafetyChecks = false)]
  88. unsafe struct NativeJobSafetyBurstST : IJob
  89. {
  90. [NativeDisableUnsafePtrRestriction] public T* methods;
  91. public void Execute() => methods->MeasureNativeContainer(0, 0);
  92. }
  93. [BurstCompile(CompileSynchronously = true, DisableSafetyChecks = true)]
  94. unsafe struct UnsafeJobBurstST : IJob
  95. {
  96. [NativeDisableUnsafePtrRestriction] public T* methods;
  97. public void Execute() => methods->MeasureUnsafeContainer(0, 0);
  98. }
  99. [BurstCompile(CompileSynchronously = true, DisableSafetyChecks = false)]
  100. unsafe struct UnsafeJobSafetyBurstST : IJob
  101. {
  102. [NativeDisableUnsafePtrRestriction] public T* methods;
  103. public void Execute() => methods->MeasureUnsafeContainer(0, 0);
  104. }
  105. unsafe struct NativeJobMT : IJobParallelFor
  106. {
  107. [NativeSetThreadIndex] int threadIndex;
  108. [NativeDisableUnsafePtrRestriction] public T* methods;
  109. public void Execute(int index) => methods->MeasureNativeContainer(index, threadIndex);
  110. }
  111. [BurstCompile(CompileSynchronously = true, DisableSafetyChecks = true)]
  112. unsafe struct NativeJobBurstMT : IJobParallelFor
  113. {
  114. [NativeSetThreadIndex] int threadIndex;
  115. [NativeDisableUnsafePtrRestriction] public T* methods;
  116. public void Execute(int index) => methods->MeasureNativeContainer(index, threadIndex);
  117. }
  118. [BurstCompile(CompileSynchronously = true, DisableSafetyChecks = false)]
  119. unsafe struct NativeJobSafetyBurstMT : IJobParallelFor
  120. {
  121. [NativeSetThreadIndex] int threadIndex;
  122. [NativeDisableUnsafePtrRestriction] public T* methods;
  123. public void Execute(int index) => methods->MeasureNativeContainer(index, threadIndex);
  124. }
  125. unsafe struct UnsafeJobMT : IJobParallelFor
  126. {
  127. [NativeSetThreadIndex] int threadIndex;
  128. [NativeDisableUnsafePtrRestriction] public T* methods;
  129. public void Execute(int index) => methods->MeasureUnsafeContainer(index, threadIndex);
  130. }
  131. [BurstCompile(CompileSynchronously = true, DisableSafetyChecks = true)]
  132. unsafe struct UnsafeJobBurstMT : IJobParallelFor
  133. {
  134. [NativeSetThreadIndex] int threadIndex;
  135. [NativeDisableUnsafePtrRestriction] public T* methods;
  136. public void Execute(int index) => methods->MeasureUnsafeContainer(index, threadIndex);
  137. }
  138. [BurstCompile(CompileSynchronously = true, DisableSafetyChecks = false)]
  139. unsafe struct UnsafeJobSafetyBurstMT : IJobParallelFor
  140. {
  141. [NativeSetThreadIndex] int threadIndex;
  142. [NativeDisableUnsafePtrRestriction] public T* methods;
  143. public void Execute(int index) => methods->MeasureUnsafeContainer(index, threadIndex);
  144. }
  145. unsafe struct BclJobMT : IJobParallelFor
  146. {
  147. [NativeDisableUnsafePtrRestriction] public T* methods;
  148. [NativeDisableUnsafePtrRestriction] public GCHandle* gcHandle;
  149. public void Execute(int index) => methods->MeasureBclContainer(gcHandle->Target, index);
  150. }
  151. static unsafe void RunMT(int workers, int capacity, BenchmarkContainerType type, params int[] args)
  152. {
  153. var methods = new T();
  154. methods.SetParams(capacity, args);
  155. switch (type)
  156. {
  157. case (BenchmarkContainerType)(BenchmarkContainerConfig.BCL):
  158. object container = null;
  159. GCHandle* gcHandle = default;
  160. BenchmarkMeasure.MeasureParallel(typeof(T),
  161. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  162. () => new BclJobMT { methods = (T*)UnsafeUtility.AddressOf(ref methods), gcHandle = gcHandle }.Schedule(workers, 1).Complete(),
  163. () =>
  164. {
  165. container = methods.AllocBclContainer(capacity);
  166. gcHandle = (GCHandle*)UnsafeUtility.Malloc(sizeof(GCHandle), 0, Allocator.Persistent);
  167. *gcHandle = GCHandle.Alloc(container);
  168. },
  169. () =>
  170. {
  171. gcHandle->Free();
  172. UnsafeUtility.Free(gcHandle, Allocator.Persistent);
  173. container = methods.AllocBclContainer(-1);
  174. });
  175. break;
  176. case BenchmarkContainerType.Native:
  177. BenchmarkMeasure.MeasureParallel(typeof(T),
  178. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  179. () => new NativeJobMT { methods = (T*)UnsafeUtility.AddressOf(ref methods) }.Schedule(workers, 1).Complete(),
  180. () => methods.AllocNativeContainer(capacity), () => methods.AllocNativeContainer(-1));
  181. break;
  182. case BenchmarkContainerType.NativeBurstSafety:
  183. BenchmarkMeasure.MeasureParallel(typeof(T),
  184. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  185. () => new NativeJobSafetyBurstMT { methods = (T*)UnsafeUtility.AddressOf(ref methods) }.Schedule(workers, 1).Complete(),
  186. () => methods.AllocNativeContainer(capacity), () => methods.AllocNativeContainer(-1));
  187. break;
  188. case BenchmarkContainerType.NativeBurstNoSafety:
  189. BenchmarkMeasure.MeasureParallel(typeof(T),
  190. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  191. () => new NativeJobBurstMT { methods = (T*)UnsafeUtility.AddressOf(ref methods) }.Schedule(workers, 1).Complete(),
  192. () => methods.AllocNativeContainer(capacity), () => methods.AllocNativeContainer(-1));
  193. break;
  194. case BenchmarkContainerType.Unsafe:
  195. BenchmarkMeasure.Measure(typeof(T),
  196. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  197. () => new UnsafeJobMT { methods = (T*)UnsafeUtility.AddressOf(ref methods) }.Schedule(workers, 1).Complete(),
  198. () => methods.AllocUnsafeContainer(capacity), () => methods.AllocUnsafeContainer(-1));
  199. break;
  200. case BenchmarkContainerType.UnsafeBurstSafety:
  201. BenchmarkMeasure.Measure(typeof(T),
  202. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  203. () => new UnsafeJobSafetyBurstMT { methods = (T*)UnsafeUtility.AddressOf(ref methods) }.Schedule(workers, 1).Complete(),
  204. () => methods.AllocUnsafeContainer(capacity), () => methods.AllocUnsafeContainer(-1));
  205. break;
  206. case BenchmarkContainerType.UnsafeBurstNoSafety:
  207. BenchmarkMeasure.MeasureParallel(typeof(T),
  208. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  209. () => new UnsafeJobBurstMT { methods = (T*)UnsafeUtility.AddressOf(ref methods) }.Schedule(workers, 1).Complete(),
  210. () => methods.AllocUnsafeContainer(capacity), () => methods.AllocUnsafeContainer(-1));
  211. break;
  212. }
  213. }
  214. /// <summary>
  215. /// Called from a typical performance test method to provide both Performance Framework measurements as well as
  216. /// Benchmark Framework measurements. A typical usage is similar to:
  217. /// <c>[Test, Performance]<br />
  218. /// [Category("Performance")]<br />
  219. /// public unsafe void ToNativeArray(<br />
  220. /// [Values(100000, 1000000, 10000000)] int capacity,<br />
  221. /// [Values] BenchmarkContainerType type)<br />
  222. /// {<br />
  223. /// BenchmarkContainerRunner&lt;HashSetToNativeArray&gt;.RunST(capacity, type);<br />
  224. /// }</c>
  225. /// </summary>
  226. /// <param name="capacity">The capacity for the container(s) which will be passed to setup methods</param>
  227. /// <param name="type">The benchmark or performance measurement type to run for containers i.e. <see cref="BenchmarkContainerType.Native"/> etc.</param>
  228. /// <param name="args">Optional arguments that can be stored in a test implementation class.</param>
  229. /// <remarks>This will run measurements with <see cref="IJob"/> or directly called on the main thread.</remarks>
  230. public static unsafe void Run(int capacity, BenchmarkContainerType type, params int[] args)
  231. {
  232. var methods = new T();
  233. methods.SetParams(capacity, args);
  234. switch (type)
  235. {
  236. case (BenchmarkContainerType)(BenchmarkContainerConfig.BCL):
  237. object container = null;
  238. BenchmarkMeasure.Measure(typeof(T),
  239. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  240. () => methods.MeasureBclContainer(container, 0),
  241. () => container = methods.AllocBclContainer(capacity), () => container = methods.AllocBclContainer(-1));
  242. break;
  243. case BenchmarkContainerType.Native:
  244. BenchmarkMeasure.Measure(typeof(T),
  245. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  246. () => methods.MeasureNativeContainer(0, 0),
  247. () => methods.AllocNativeContainer(capacity), () => methods.AllocNativeContainer(-1));
  248. break;
  249. case BenchmarkContainerType.NativeBurstSafety:
  250. BenchmarkMeasure.Measure(typeof(T),
  251. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  252. () => new NativeJobSafetyBurstST { methods = (T*)UnsafeUtility.AddressOf(ref methods) }.Run(),
  253. () => methods.AllocNativeContainer(capacity), () => methods.AllocNativeContainer(-1));
  254. break;
  255. case BenchmarkContainerType.NativeBurstNoSafety:
  256. BenchmarkMeasure.Measure(typeof(T),
  257. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  258. () => new NativeJobBurstST { methods = (T*)UnsafeUtility.AddressOf(ref methods) }.Run(),
  259. () => methods.AllocNativeContainer(capacity), () => methods.AllocNativeContainer(-1));
  260. break;
  261. case BenchmarkContainerType.Unsafe:
  262. BenchmarkMeasure.Measure(typeof(T),
  263. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  264. () => methods.MeasureUnsafeContainer(0, 0),
  265. () => methods.AllocUnsafeContainer(capacity), () => methods.AllocUnsafeContainer(-1));
  266. break;
  267. case BenchmarkContainerType.UnsafeBurstSafety:
  268. BenchmarkMeasure.Measure(typeof(T),
  269. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  270. () => new UnsafeJobSafetyBurstST { methods = (T*)UnsafeUtility.AddressOf(ref methods) }.Run(),
  271. () => methods.AllocUnsafeContainer(capacity), () => methods.AllocUnsafeContainer(-1));
  272. break;
  273. case BenchmarkContainerType.UnsafeBurstNoSafety:
  274. BenchmarkMeasure.Measure(typeof(T),
  275. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  276. () => new UnsafeJobBurstST { methods = (T*)UnsafeUtility.AddressOf(ref methods) }.Run(),
  277. () => methods.AllocUnsafeContainer(capacity), () => methods.AllocUnsafeContainer(-1));
  278. break;
  279. }
  280. }
  281. /// <summary>
  282. /// Called from a typical performance test method to provide both Performance Framework measurements as well as
  283. /// Benchmark Framework measurements. A typical usage is similar to:
  284. /// <c>[Test, Performance]<br />
  285. /// [Category("Performance")]<br />
  286. /// public unsafe void ToNativeArray(<br />
  287. /// [Values(1, 2, 4, 8)] int workers,<br />
  288. /// [Values(100000, 1000000, 10000000)] int capacity,<br />
  289. /// [Values] BenchmarkContainerType type)<br />
  290. /// {<br />
  291. /// BenchmarkContainerRunner&lt;HashSetToNativeArray&gt;.Run(workers, capacity, type);<br />
  292. /// }</c>
  293. /// </summary>
  294. /// <param name="workers">The number of job workers to run performance tests on. These are duplicated across workers rather than split across workers.</param>
  295. /// <param name="capacity">The capacity for the container(s) which will be passed to setup methods</param>
  296. /// <param name="type">The benchmark or performance measurement type to run for containers i.e. <see cref="BenchmarkContainerType.Native"/> etc.</param>
  297. /// <param name="args">Optional arguments that can be stored in a test implementation class.</param>
  298. /// <remarks>This will run measurements with <see cref="IJob"/> or <see cref="IJobParallelFor"/> based on the number of workers being 1 or 2+, respectively.</remarks>
  299. public static unsafe void Run(int workers, int capacity, BenchmarkContainerType type, params int[] args)
  300. {
  301. if (workers == 1)
  302. Run(capacity, type, args);
  303. else
  304. RunMT(workers, capacity, type, args);
  305. }
  306. }
  307. }