A game about forced loneliness, made by TACStudios
1using System; 2using Unity.Collections; 3using Unity.Collections.LowLevel.Unsafe; 4 5namespace Unity.PerformanceTesting.Benchmark 6{ 7 /// <summary> 8 /// Specifies the statistic used for benchmark comparisons. 9 /// </summary> 10 public enum BenchmarkRankingStatistic 11 { 12 /// <summary>Compare the minimum time from a set of samples</summary> 13 Min, 14 /// <summary>Compare the maximum time from a set of samples</summary> 15 Max, 16 /// <summary>Compare the median time from a set of samples</summary> 17 Median, 18 /// <summary>Compare the average time from a set of samples</summary> 19 Average, 20 /// <summary>Compare the standard deviation of time from a set of samples</summary> 21 StdDev, 22 /// <summary>Compare the sum time of a set of samples</summary> 23 Sum, 24 } 25 26 internal enum BenchmarkResultType 27 { 28 Ignored, 29 Normal, 30 NormalBaseline, 31 External, 32 ExternalBaseline, 33 } 34 35 internal enum BenchmarkRankingType 36 { 37 Ignored, 38 Normal, 39 Best, 40 Worst, 41 } 42 43 internal struct BenchmarkResults 44 { 45 public const uint kFlagNoOptimization = 0x01; 46 public const uint kFlagParallelJobs = 0x02; 47 public const uint kFlagFootnotes = 0x04; // this must always be the last predefined flag bit 48 49 public SampleUnit unit; 50 public double min; 51 public double max; 52 public double median; 53 public double average; 54 public double standardDeviation; 55 public double sum; 56 public BenchmarkRankingType ranking; 57 public BenchmarkRankingStatistic statistic; 58 public double baselineRatio; 59 public uint resultFlags; 60 61 internal static readonly BenchmarkResults Ignored = new BenchmarkResults { ranking = BenchmarkRankingType.Ignored }; 62 63 internal BenchmarkResults(SampleGroup sampleGroup, BenchmarkRankingStatistic rankingStatistic, uint flags) 64 { 65 unit = sampleGroup.Unit; 66 min = sampleGroup.Min; 67 max = sampleGroup.Max; 68 median = sampleGroup.Median; 69 average = sampleGroup.Average; 70 standardDeviation = sampleGroup.StandardDeviation; 71 sum = sampleGroup.Sum; 72 ranking = BenchmarkRankingType.Normal; 73 statistic = rankingStatistic; 74 baselineRatio = 0; 75 resultFlags = flags; 76 } 77 78 public double Comparator 79 { 80 get 81 { 82 switch (statistic) 83 { 84 case BenchmarkRankingStatistic.Min: return min; 85 case BenchmarkRankingStatistic.Max: return max; 86 case BenchmarkRankingStatistic.Median: return median; 87 case BenchmarkRankingStatistic.Average: return average; 88 case BenchmarkRankingStatistic.StdDev: return standardDeviation; 89 case BenchmarkRankingStatistic.Sum: return sum; 90 } 91 return median; 92 } 93 } 94 95 public string UnitSuffix 96 { 97 get 98 { 99 switch (unit) 100 { 101 case SampleUnit.Nanosecond: return "ns"; 102 case SampleUnit.Microsecond: return "µs"; 103 case SampleUnit.Millisecond: return "ms"; 104 case SampleUnit.Second: return "s"; 105 case SampleUnit.Byte: return "b"; 106 case SampleUnit.Kilobyte: return "kb"; 107 case SampleUnit.Megabyte: return "mb"; 108 case SampleUnit.Gigabyte: return "gb"; 109 case SampleUnit.Undefined: 110 break; 111 } 112 return ""; 113 } 114 } 115 } 116 117 internal struct BenchmarkReportComparison : IDisposable 118 { 119 public UnsafeList<BenchmarkResults> results; 120 public FixedString512Bytes comparisonName; 121 public uint footnoteFlags; 122 123 public BenchmarkReportComparison(string name) 124 { 125 results = new UnsafeList<BenchmarkResults>(1, Allocator.Persistent); 126 comparisonName = name; 127 footnoteFlags = 0; 128 } 129 130 public void Dispose() 131 { 132 if (results.IsCreated) 133 results.Dispose(); 134 } 135 136 public void RankResults(BenchmarkResultType[] resultTypes) 137 { 138 double min = double.MaxValue; 139 double max = double.MinValue; 140 int baselineJ = -1; 141 int firstJ = -1; 142 143 for (int j = 0; j < results.Length; j++) 144 { 145 if (results[j].ranking == BenchmarkRankingType.Ignored) 146 continue; 147 if (firstJ == -1) 148 firstJ = j; 149 150 double result = results[j].Comparator; 151 if (result < min) 152 min = result; 153 if (result > max) 154 max = result; 155 156 if (resultTypes[j] == BenchmarkResultType.ExternalBaseline || resultTypes[j] == BenchmarkResultType.NormalBaseline) 157 { 158 if (baselineJ == -1) 159 baselineJ = j; 160 else 161 throw new Exception("[INTERNAL ERROR] More than one baseline found - this should have been caught during initialization"); 162 } 163 } 164 165 bool same = true; 166 for (int prevJ = firstJ, j = firstJ + 1; j < results.Length; j++) 167 { 168 if (results[j].ranking == BenchmarkRankingType.Ignored) 169 continue; 170 if (results[prevJ].Comparator != results[j].Comparator) 171 same = false; 172 prevJ = j; 173 } 174 if (!same) 175 { 176 for (int j = 0; j < results.Length; j++) 177 { 178 if (results[j].ranking == BenchmarkRankingType.Ignored) 179 continue; 180 if (results[j].Comparator == min) 181 results.ElementAt(j).ranking = BenchmarkRankingType.Best; 182 else if (results[j].Comparator == max) 183 results.ElementAt(j).ranking = BenchmarkRankingType.Worst; 184 } 185 } 186 187 if (baselineJ == -1) 188 throw new Exception("[INTERNAL ERROR] No baseline found - this should have been caught during initialization"); 189 190 for (int j = 0; j < results.Length; j++) 191 { 192 if (results[j].ranking == BenchmarkRankingType.Ignored) 193 continue; 194 if (results[j].Comparator != 0) 195 results.ElementAt(j).baselineRatio = results[baselineJ].Comparator / results[j].Comparator; 196 } 197 } 198 } 199 200 internal struct BenchmarkReportGroup : IDisposable 201 { 202 public UnsafeList<BenchmarkReportComparison> comparisons; 203 public FixedString512Bytes groupName; 204 public UnsafeList<FixedString64Bytes> variantNames; 205 public UnsafeList<BenchmarkResultType> resultTypes; 206 public int resultDecimalPlaces; 207 public UnsafeHashMap<uint, NativeText> customFootnotes; 208 209 public BenchmarkReportGroup(string name, string[] variantNameArray, BenchmarkResultType[] resultTypeArray, int resultDecimalPlaces) 210 { 211 comparisons = new UnsafeList<BenchmarkReportComparison>(1, Allocator.Persistent); 212 groupName = name; 213 variantNames = new UnsafeList<FixedString64Bytes>(variantNameArray.Length, Allocator.Persistent); 214 resultTypes = new UnsafeList<BenchmarkResultType>(resultTypeArray.Length, Allocator.Persistent); 215 this.resultDecimalPlaces = resultDecimalPlaces; 216 foreach (var title in variantNameArray) 217 variantNames.Add(title); 218 foreach (var resultType in resultTypeArray) 219 resultTypes.Add(resultType); 220 customFootnotes = new UnsafeHashMap<uint, NativeText>(30, Allocator.Persistent); 221 } 222 223 public void Dispose() 224 { 225 if (comparisons.IsCreated) 226 { 227 for (int i = 0; i < comparisons.Length; i++) 228 comparisons[i].Dispose(); 229 comparisons.Dispose(); 230 } 231 if (variantNames.IsCreated) 232 variantNames.Dispose(); 233 if (customFootnotes.IsCreated) 234 { 235 foreach (var pair in customFootnotes) 236 pair.Value.Dispose(); 237 customFootnotes.Dispose(); 238 } 239 } 240 } 241 242 internal struct BenchmarkReports : IDisposable 243 { 244 public UnsafeList<BenchmarkReportGroup> groups; 245 public FixedString512Bytes reportName; 246 247 public BenchmarkReports(string name) 248 { 249 groups = new UnsafeList<BenchmarkReportGroup>(1, Allocator.Persistent); 250 reportName = name; 251 } 252 253 public void Dispose() 254 { 255 if (groups.IsCreated) 256 { 257 for (int i = 0; i < groups.Length; i++) 258 groups[i].Dispose(); 259 groups.Dispose(); 260 } 261 } 262 } 263}