Aucune description
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

BenchmarkContainer.cs 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. using Unity.PerformanceTesting;
  2. using Unity.PerformanceTesting.Benchmark;
  3. using Unity.Burst;
  4. using Unity.Collections.LowLevel.Unsafe;
  5. using Unity.Jobs;
  6. namespace Unity.Collections.PerformanceTests
  7. {
  8. /// <summary>
  9. /// Specifies a class containing performance test methods which should be included in container benchmarking.<para />
  10. /// The values specified in this enum are unlikely to be needed in user code, but user code will specify the enum type
  11. /// in a couple places:<para />
  12. /// <c>[Benchmark(typeof(BenchmarkContainerType))] // &lt;---- HERE<br />
  13. /// class FooContainerPerformanceTestMethods</c><para />
  14. /// and<para />
  15. /// <c>[Test, Performance]<br />
  16. /// public unsafe void ContainerPerfTestExample(<br />
  17. /// [Values(100000, 1000000, 10000000)] int capacity,<br />
  18. /// [Values] BenchmarkContainerType type) // &lt;---- HERE<br />
  19. /// {</c><para />
  20. /// Though values may be specified in the performance test method parameter, it is recommended to leave the argument implicitly
  21. /// covering all enum values as seen in the example above.
  22. /// </summary>
  23. [BenchmarkComparison(BenchmarkContainerConfig.BCL, "{0} (BCL)")]
  24. [BenchmarkComparisonDisplay(SampleUnit.Millisecond, 3, BenchmarkContainerConfig.kRankingMethod)]
  25. public enum BenchmarkContainerType : int
  26. {
  27. /// <summary>Native container performance test will execute on a managed (not burst compiled) code path</summary>
  28. [BenchmarkName("Native{0} (S)")] Native,
  29. /// <summary>Native container performance test will execute on a burst compile code path, with safety checks enabled</summary>
  30. [BenchmarkName("Native{0} (S+B)")] NativeBurstSafety,
  31. /// <summary>Native container performance test will execute on a burst compile code path, with safety checks disabled</summary>
  32. [BenchmarkName("Native{0} (B)")] NativeBurstNoSafety,
  33. /// <summary>Unsafe container performance test will execute on a managed (not burst compiled) code path</summary>
  34. [BenchmarkName("Unsafe{0} (S)")] Unsafe,
  35. /// <summary>Unsafe container performance test will execute on a burst compile code path, with safety checks enabled</summary>
  36. [BenchmarkName("Unsafe{0} (S+B)")] UnsafeBurstSafety,
  37. /// <summary>Unsafe container performance test will execute on a burst compile code path, with safety checks disabled</summary>
  38. [BenchmarkName("Unsafe{0} (B)")] UnsafeBurstNoSafety,
  39. }
  40. /// <summary>
  41. /// Configuration settings for benchmarking containers.
  42. /// </summary>
  43. public static class BenchmarkContainerConfig
  44. {
  45. /// <summary>
  46. /// An additional value to the enum values defined in <see cref="BenchmarkContainerType"/> which will not be included
  47. /// in Performance Test Framework test generation but will be included in Benchmark Framework result generation.
  48. /// </summary>
  49. public const int BCL = -1;
  50. internal const BenchmarkRankingStatistic kRankingMethod = BenchmarkRankingStatistic.Median;
  51. internal const int kCountWarmup = 5;
  52. internal const int kCountMeasure = 10;
  53. /// <summary>
  54. /// Prefix string for individual benchmark menu items
  55. /// </summary>
  56. public const string kMenuItemIndividual = "DOTS/Unity.Collections/Generate Individual Container Benchmark/";
  57. #if UNITY_EDITOR
  58. [UnityEditor.MenuItem("DOTS/Unity.Collections/Generate Container Benchmarks")]
  59. #endif
  60. static void RunBenchmarks() =>
  61. BenchmarkGenerator.GenerateMarkdown(
  62. "Containers",
  63. typeof(BenchmarkContainerType),
  64. "../../Packages/com.unity.collections/Documentation~/performance-comparison-containers.md",
  65. $"The **{kRankingMethod} of {kCountMeasure} sample sets** is compared against the baseline on the far right side of the table."
  66. + $"<br/>Multithreaded benchmarks divide the processing amongst the specified number of workers."
  67. + $"<br/>{kCountWarmup} extra sample sets are run as warmup."
  68. ,
  69. "Legend",
  70. new string[]
  71. {
  72. "`(S)` = Safety Enabled",
  73. "`(B)` = Burst Compiled *with Safety Disabled*",
  74. "`(S+B)` = Burst Compiled *with Safety Enabled*",
  75. "`(BCL)` = Base Class Library implementation (such as provided by Mono or .NET)",
  76. "",
  77. "*`italic`* results are for benchmarking comparison only; these are not included in standard Performance Framework tests",
  78. });
  79. #if UNITY_EDITOR
  80. /// <summary>
  81. /// Runs a benchmark for a particular class with Performance Test Framework/Benchmark methods. For calling
  82. /// from custom menu items, for instance.
  83. /// </summary>
  84. /// <param name="testClassType"></param>
  85. public static void RunBenchmark(System.Type testClassType)
  86. {
  87. string tempPath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(
  88. UnityEditor.FileUtil.GetUniqueTempPathInProject()), $"Benchmark{testClassType.Name}.md");
  89. BenchmarkGenerator.GenerateMarkdown(
  90. $"Individual -- {testClassType.Name}",
  91. new System.Type[] { testClassType },
  92. tempPath,
  93. $"The **{kRankingMethod} of {kCountMeasure} sample sets** is compared against the baseline on the far right side of the table."
  94. + $"<br/>Multithreaded benchmarks divide the processing amongst the specified number of workers."
  95. + $"<br/>{kCountWarmup} extra sample sets are run as warmup."
  96. ,
  97. "Legend",
  98. new string[]
  99. {
  100. "`(S)` = Safety Enabled",
  101. "`(B)` = Burst Compiled *with Safety Disabled*",
  102. "`(S+B)` = Burst Compiled *with Safety Enabled*",
  103. "`(BCL)` = Base Class Library implementation (such as provided by Mono or .NET)",
  104. "",
  105. "*`italic`* results are for benchmarking comparison only; these are not included in standard Performance Framework tests",
  106. });
  107. UnityEditor.EditorUtility.RevealInFinder(tempPath);
  108. }
  109. #endif
  110. }
  111. /// <summary>
  112. /// Interface to implement container performance tests which will run using <see cref="BenchmarkContainerRunner{T}.Run(int, BenchmarkContainerType, int[])"/>.
  113. /// Deriving tests from this interface enables both Performance Test Framework and Benchmark Framework to generate and run
  114. /// tests for the contexts described by <see cref="BenchmarkContainerType"/>.
  115. /// </summary>
  116. public interface IBenchmarkContainer
  117. {
  118. /// <summary>
  119. /// Override this to add extra int arguments to a performance test implementation as fields in the implementing type. These arguments
  120. /// are optionally passed in through <see cref="BenchmarkContainerRunner{T}.Run(int, BenchmarkContainerType, int[])"/>.
  121. /// </summary>
  122. /// <param name="capacity">The initial capacity to requested for the container.</param>
  123. /// <param name="args">A variable number of extra arguments to passed through to the test implementation</param>
  124. public void SetParams(int capacity, params int[] args) { }
  125. /// <summary>
  126. /// Called during setup for each measurement in a sample set with the capacity to allocate to the native container
  127. /// when the benchmark type is <see cref="BenchmarkContainerType.Native"/>, <see cref="BenchmarkContainerType.NativeBurstNoSafety"/>,
  128. /// or <see cref="BenchmarkContainerType.NativeBurstSafety"/>.<para />
  129. /// This is also called during teardown for each measurement in a sample set with '-1' to indicate freeing the container.
  130. /// </summary>
  131. /// <param name="capacity">The capacity to allocate for the managed container. Capacity of 0 will still create a container,
  132. /// but it will be empty. A capacity of -1 will dispose the container and free associated allocation(s).</param>
  133. public void AllocNativeContainer(int capacity);
  134. /// <summary>
  135. /// Called during setup for each measurement in a sample set with the capacity to allocate to the unsafe container
  136. /// when the benchmark type is <see cref="BenchmarkContainerType.Unsafe"/>, <see cref="BenchmarkContainerType.UnsafeBurstNoSafety"/>,
  137. /// or <see cref="BenchmarkContainerType.UnsafeBurstSafety"/>.<para />
  138. /// This is also called during teardown for each measurement in a sample set with '-1' to indicate freeing the container.
  139. /// </summary>
  140. /// <param name="capacity">The capacity to allocate for the managed container. Capacity of 0 will still create a container,
  141. /// but it will be empty. A capacity of -1 will dispose the container and free associated allocation(s).</param>
  142. public void AllocUnsafeContainer(int capacity); // capacity 0 frees
  143. /// <summary>
  144. /// Called during setup for each measurement in a sample set with the capacity to allocate to the managed container
  145. /// when the benchmark type is <see cref="BenchmarkContainerConfig.BCL"/>.<para />
  146. /// This is also called during teardown for each measurement in a sample set with '-1' to indicate freeing the container.
  147. /// </summary>
  148. /// <param name="capacity">The capacity to allocate for the managed container. Capacity of 0 will still create a container,
  149. /// but it will be empty. A capacity of -1 will dispose the container and free associated allocation(s).</param>
  150. /// <returns>A reference to the allocated container when capacity &gt;= 0, and `null` when capacity &lt; 0.</returns>
  151. public object AllocBclContainer(int capacity);
  152. /// <summary>
  153. /// The code which will be executed during performance measurement. This should usually be general enough to
  154. /// work with any native container.
  155. /// </summary>
  156. public void MeasureNativeContainer();
  157. /// <summary>
  158. /// The code which will be executed during performance measurement. This should usually be general enough to
  159. /// work with any unsafe container.
  160. /// </summary>
  161. public void MeasureUnsafeContainer();
  162. /// <summary>
  163. /// The code which will be executed during performance measurement. This should usually be general enough to
  164. /// work with any managed container provided by the Base Class Library (BCL).
  165. /// </summary>
  166. /// <param name="container">A reference to the managed container allocated in <see cref="AllocBclContainer(int)"/></param>
  167. public void MeasureBclContainer(object container);
  168. }
  169. /// <summary>
  170. /// Provides the API for running container based Performance Framework tests and Benchmark Framework measurements.
  171. /// This will typically be the sole call from a performance test. See <see cref="Run(int, BenchmarkContainerType, int[])"/>
  172. /// for more information.
  173. /// </summary>
  174. /// <typeparam name="T">An implementation conforming to the <see cref="IBenchmarkContainer"/> interface for running container performance tests and benchmarks.</typeparam>
  175. [BurstCompile(CompileSynchronously = true)]
  176. public static class BenchmarkContainerRunner<T> where T : unmanaged, IBenchmarkContainer
  177. {
  178. [BurstCompile(CompileSynchronously = true, DisableSafetyChecks = true)]
  179. unsafe struct NativeJobBurstST : IJob
  180. {
  181. [NativeDisableUnsafePtrRestriction] public T* methods;
  182. public void Execute() => methods->MeasureNativeContainer();
  183. }
  184. [BurstCompile(CompileSynchronously = true, DisableSafetyChecks = false)]
  185. unsafe struct NativeJobSafetyBurstST : IJob
  186. {
  187. [NativeDisableUnsafePtrRestriction] public T* methods;
  188. public void Execute() => methods->MeasureNativeContainer();
  189. }
  190. [BurstCompile(CompileSynchronously = true, DisableSafetyChecks = true)]
  191. unsafe struct UnsafeJobBurstST : IJob
  192. {
  193. [NativeDisableUnsafePtrRestriction] public T* methods;
  194. public void Execute() => methods->MeasureUnsafeContainer();
  195. }
  196. [BurstCompile(CompileSynchronously = true, DisableSafetyChecks = false)]
  197. unsafe struct UnsafeJobSafetyBurstST : IJob
  198. {
  199. [NativeDisableUnsafePtrRestriction] public T* methods;
  200. public void Execute() => methods->MeasureUnsafeContainer();
  201. }
  202. /// <summary>
  203. /// Called from a typical performance test method to provide both Performance Framework measurements as well as
  204. /// Benchmark Framework measurements. A typical usage is similar to:
  205. /// <c>[Test, Performance]<br />
  206. /// [Category("Performance")]<br />
  207. /// public unsafe void ToNativeArray(<br />
  208. /// [Values(100000, 1000000, 10000000)] int capacity,<br />
  209. /// [Values] BenchmarkContainerType type)<br />
  210. /// {<br />
  211. /// BenchmarkContainerRunner&lt;HashSetToNativeArray&gt;.RunST(capacity, type);<br />
  212. /// }</c>
  213. /// </summary>
  214. /// <param name="capacity">The capacity for the container(s) which will be passed to setup methods</param>
  215. /// <param name="type">The benchmark or performance measurement type to run for containers i.e. <see cref="BenchmarkContainerType.Native"/> etc.</param>
  216. /// <param name="args">Optional arguments that can be stored in a test implementation class.</param>
  217. /// <remarks>This will run measurements with <see cref="IJob"/> or directly called on the main thread.</remarks>
  218. public static unsafe void Run(int capacity, BenchmarkContainerType type, params int[] args)
  219. {
  220. var methods = new T();
  221. methods.SetParams(capacity, args);
  222. switch (type)
  223. {
  224. case (BenchmarkContainerType)(BenchmarkContainerConfig.BCL):
  225. object container = null;
  226. BenchmarkMeasure.Measure(typeof(T),
  227. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  228. () => methods.MeasureBclContainer(container),
  229. () => container = methods.AllocBclContainer(capacity), () => container = methods.AllocBclContainer(-1));
  230. break;
  231. case BenchmarkContainerType.Native:
  232. BenchmarkMeasure.Measure(typeof(T),
  233. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  234. () => methods.MeasureNativeContainer(),
  235. () => methods.AllocNativeContainer(capacity), () => methods.AllocNativeContainer(-1));
  236. break;
  237. case BenchmarkContainerType.NativeBurstSafety:
  238. BenchmarkMeasure.Measure(typeof(T),
  239. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  240. () => new NativeJobSafetyBurstST { methods = (T*)UnsafeUtility.AddressOf(ref methods) }.Run(),
  241. () => methods.AllocNativeContainer(capacity), () => methods.AllocNativeContainer(-1));
  242. break;
  243. case BenchmarkContainerType.NativeBurstNoSafety:
  244. BenchmarkMeasure.Measure(typeof(T),
  245. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  246. () => new NativeJobBurstST { methods = (T*)UnsafeUtility.AddressOf(ref methods) }.Run(),
  247. () => methods.AllocNativeContainer(capacity), () => methods.AllocNativeContainer(-1));
  248. break;
  249. case BenchmarkContainerType.Unsafe:
  250. BenchmarkMeasure.Measure(typeof(T),
  251. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  252. () => methods.MeasureUnsafeContainer(),
  253. () => methods.AllocUnsafeContainer(capacity), () => methods.AllocUnsafeContainer(-1));
  254. break;
  255. case BenchmarkContainerType.UnsafeBurstSafety:
  256. BenchmarkMeasure.Measure(typeof(T),
  257. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  258. () => new UnsafeJobSafetyBurstST { methods = (T*)UnsafeUtility.AddressOf(ref methods) }.Run(),
  259. () => methods.AllocUnsafeContainer(capacity), () => methods.AllocUnsafeContainer(-1));
  260. break;
  261. case BenchmarkContainerType.UnsafeBurstNoSafety:
  262. BenchmarkMeasure.Measure(typeof(T),
  263. BenchmarkContainerConfig.kCountWarmup, BenchmarkContainerConfig.kCountMeasure,
  264. () => new UnsafeJobBurstST { methods = (T*)UnsafeUtility.AddressOf(ref methods) }.Run(),
  265. () => methods.AllocUnsafeContainer(capacity), () => methods.AllocUnsafeContainer(-1));
  266. break;
  267. }
  268. }
  269. }
  270. }