using System; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; namespace Unity.PerformanceTesting.Benchmark { /// /// Specifies the statistic used for benchmark comparisons. /// public enum BenchmarkRankingStatistic { /// Compare the minimum time from a set of samples Min, /// Compare the maximum time from a set of samples Max, /// Compare the median time from a set of samples Median, /// Compare the average time from a set of samples Average, /// Compare the standard deviation of time from a set of samples StdDev, /// Compare the sum time of a set of samples Sum, } internal enum BenchmarkResultType { Ignored, Normal, NormalBaseline, External, ExternalBaseline, } internal enum BenchmarkRankingType { Ignored, Normal, Best, Worst, } internal struct BenchmarkResults { public const uint kFlagNoOptimization = 0x01; public const uint kFlagParallelJobs = 0x02; public const uint kFlagFootnotes = 0x04; // this must always be the last predefined flag bit public SampleUnit unit; public double min; public double max; public double median; public double average; public double standardDeviation; public double sum; public BenchmarkRankingType ranking; public BenchmarkRankingStatistic statistic; public double baselineRatio; public uint resultFlags; internal static readonly BenchmarkResults Ignored = new BenchmarkResults { ranking = BenchmarkRankingType.Ignored }; internal BenchmarkResults(SampleGroup sampleGroup, BenchmarkRankingStatistic rankingStatistic, uint flags) { unit = sampleGroup.Unit; min = sampleGroup.Min; max = sampleGroup.Max; median = sampleGroup.Median; average = sampleGroup.Average; standardDeviation = sampleGroup.StandardDeviation; sum = sampleGroup.Sum; ranking = BenchmarkRankingType.Normal; statistic = rankingStatistic; baselineRatio = 0; resultFlags = flags; } public double Comparator { get { switch (statistic) { case BenchmarkRankingStatistic.Min: return min; case BenchmarkRankingStatistic.Max: return max; case BenchmarkRankingStatistic.Median: return median; case BenchmarkRankingStatistic.Average: return average; case BenchmarkRankingStatistic.StdDev: return standardDeviation; case BenchmarkRankingStatistic.Sum: return sum; } return median; } } public string UnitSuffix { get { switch (unit) { case SampleUnit.Nanosecond: return "ns"; case SampleUnit.Microsecond: return "µs"; case SampleUnit.Millisecond: return "ms"; case SampleUnit.Second: return "s"; case SampleUnit.Byte: return "b"; case SampleUnit.Kilobyte: return "kb"; case SampleUnit.Megabyte: return "mb"; case SampleUnit.Gigabyte: return "gb"; case SampleUnit.Undefined: break; } return ""; } } } internal struct BenchmarkReportComparison : IDisposable { public UnsafeList results; public FixedString512Bytes comparisonName; public uint footnoteFlags; public BenchmarkReportComparison(string name) { results = new UnsafeList(1, Allocator.Persistent); comparisonName = name; footnoteFlags = 0; } public void Dispose() { if (results.IsCreated) results.Dispose(); } public void RankResults(BenchmarkResultType[] resultTypes) { double min = double.MaxValue; double max = double.MinValue; int baselineJ = -1; int firstJ = -1; for (int j = 0; j < results.Length; j++) { if (results[j].ranking == BenchmarkRankingType.Ignored) continue; if (firstJ == -1) firstJ = j; double result = results[j].Comparator; if (result < min) min = result; if (result > max) max = result; if (resultTypes[j] == BenchmarkResultType.ExternalBaseline || resultTypes[j] == BenchmarkResultType.NormalBaseline) { if (baselineJ == -1) baselineJ = j; else throw new Exception("[INTERNAL ERROR] More than one baseline found - this should have been caught during initialization"); } } bool same = true; for (int prevJ = firstJ, j = firstJ + 1; j < results.Length; j++) { if (results[j].ranking == BenchmarkRankingType.Ignored) continue; if (results[prevJ].Comparator != results[j].Comparator) same = false; prevJ = j; } if (!same) { for (int j = 0; j < results.Length; j++) { if (results[j].ranking == BenchmarkRankingType.Ignored) continue; if (results[j].Comparator == min) results.ElementAt(j).ranking = BenchmarkRankingType.Best; else if (results[j].Comparator == max) results.ElementAt(j).ranking = BenchmarkRankingType.Worst; } } if (baselineJ == -1) throw new Exception("[INTERNAL ERROR] No baseline found - this should have been caught during initialization"); for (int j = 0; j < results.Length; j++) { if (results[j].ranking == BenchmarkRankingType.Ignored) continue; if (results[j].Comparator != 0) results.ElementAt(j).baselineRatio = results[baselineJ].Comparator / results[j].Comparator; } } } internal struct BenchmarkReportGroup : IDisposable { public UnsafeList comparisons; public FixedString512Bytes groupName; public UnsafeList variantNames; public UnsafeList resultTypes; public int resultDecimalPlaces; public UnsafeHashMap customFootnotes; public BenchmarkReportGroup(string name, string[] variantNameArray, BenchmarkResultType[] resultTypeArray, int resultDecimalPlaces) { comparisons = new UnsafeList(1, Allocator.Persistent); groupName = name; variantNames = new UnsafeList(variantNameArray.Length, Allocator.Persistent); resultTypes = new UnsafeList(resultTypeArray.Length, Allocator.Persistent); this.resultDecimalPlaces = resultDecimalPlaces; foreach (var title in variantNameArray) variantNames.Add(title); foreach (var resultType in resultTypeArray) resultTypes.Add(resultType); customFootnotes = new UnsafeHashMap(30, Allocator.Persistent); } public void Dispose() { if (comparisons.IsCreated) { for (int i = 0; i < comparisons.Length; i++) comparisons[i].Dispose(); comparisons.Dispose(); } if (variantNames.IsCreated) variantNames.Dispose(); if (customFootnotes.IsCreated) { foreach (var pair in customFootnotes) pair.Value.Dispose(); customFootnotes.Dispose(); } } } internal struct BenchmarkReports : IDisposable { public UnsafeList groups; public FixedString512Bytes reportName; public BenchmarkReports(string name) { groups = new UnsafeList(1, Allocator.Persistent); reportName = name; } public void Dispose() { if (groups.IsCreated) { for (int i = 0; i < groups.Length; i++) groups[i].Dispose(); groups.Dispose(); } } } }