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}