/// <summary> /// there might be some hardware counter events not belonging to the benchmarked code (for example CLR or BenchmarkDotNet's Engine) /// to calculate the % per IP we need to know the total per benchmark, not per process /// </summary> private static Dictionary <HardwareCounter, (ulong withoutNoise, ulong total)> SumHardwareCountersStatsOfBenchmarkedCode( DisassemblyResult disassemblyResult, PmcStats pmcStats) { IEnumerable <ulong> Range(Asm asm) { // most probably asm.StartAddress would be enough, but I don't want to miss any edge case for (ulong instructionPointer = asm.StartAddress; instructionPointer < asm.EndAddress; instructionPointer++) { yield return(instructionPointer); } } var instructionPointers = new HashSet <ulong>( disassemblyResult .Methods .SelectMany(method => method.Maps) .SelectMany(map => map.Instructions) .OfType <Asm>() .SelectMany(Range) .Distinct()); return(pmcStats.Counters.ToDictionary(data => data.Key, data => { ulong withoutNoise = 0, total = 0; foreach (var ipToCount in data.Value.PerInstructionPointer) { total += ipToCount.Value; if (instructionPointers.Contains(ipToCount.Key)) { withoutNoise += ipToCount.Value; } } return (withoutNoise, total); })); }
private static string Export(Summary summary, BenchmarkCase benchmarkCase, DisassemblyResult disassemblyResult, PmcStats pmcStats) { string filePath = $"{Path.Combine(summary.ResultsDirectoryPath, benchmarkCase.Descriptor.WorkloadMethod.Name)}-{benchmarkCase.Job.Environment.Jit}-{benchmarkCase.Job.Environment.Platform}-instructionPointer.html"; if (File.Exists(filePath)) { File.Delete(filePath); } var totals = SumHardwareCountersStatsOfBenchmarkedCode(disassemblyResult, pmcStats); var perMethod = SumHardwareCountersPerMethod(disassemblyResult, pmcStats); using (var stream = new StreamWriter(filePath, append: false)) { using (var streamLogger = new StreamLogger(stream)) { Export(streamLogger, benchmarkCase, totals, perMethod, pmcStats.Counters.Keys.ToArray()); } } return(filePath); }
private string Export(Summary summary, Benchmark benchmark, DisassemblyResult disassemblyResult, PmcStats pmcStats) { var filePath = $"{Path.Combine(summary.ResultsDirectoryPath, benchmark.Target.Method.Name)}-{benchmark.Job.Env.Jit}-{benchmark.Job.Env.Platform}-instructionPointer.html"; if (File.Exists(filePath)) { File.Delete(filePath); } checked { var totals = SumHardwareCountersStatsOfBenchmarkedCode(disassemblyResult, pmcStats); var perMethod = SumHardwareCountersPerMethod(disassemblyResult, pmcStats); using (var stream = Portability.StreamWriter.FromPath(filePath)) { Export(new StreamLogger(stream), benchmark, totals, perMethod, pmcStats.Counters.Keys.ToArray()); } } return(filePath); }
private static IReadOnlyList <MethodWithCounters> SumHardwareCountersPerMethod(DisassemblyResult disassemblyResult, PmcStats pmcStats) { var model = new List <MethodWithCounters>(disassemblyResult.Methods.Length); foreach (var method in disassemblyResult.Methods.Where(method => string.IsNullOrEmpty(method.Problem))) { var groups = new List <List <CodeWithCounters> >(); foreach (var map in method.Maps) { var codeWithCounters = new List <CodeWithCounters>(map.Instructions.Length); foreach (var instruction in map.Instructions) { var totalsPerCounter = pmcStats.Counters.Keys.ToDictionary(key => key, _ => default(ulong)); if (instruction is Asm asm) { foreach (var hardwareCounter in pmcStats.Counters) { // most probably asm.StartAddress would be enough, but I don't want to miss any edge case for (ulong instructionPointer = asm.StartAddress; instructionPointer < asm.EndAddress; instructionPointer++) { if (hardwareCounter.Value.PerInstructionPointer.TryGetValue(instructionPointer, out ulong value)) { totalsPerCounter[hardwareCounter.Key] = totalsPerCounter[hardwareCounter.Key] + value; } } } } codeWithCounters.Add(new CodeWithCounters { Code = instruction, SumPerCounter = totalsPerCounter }); } groups.Add(codeWithCounters); } model.Add(new MethodWithCounters { Method = method, Instructions = groups, SumPerCounter = pmcStats.Counters.Keys.ToDictionary( hardwareCounter => hardwareCounter, hardwareCounter => { ulong sum = 0; foreach (var group in groups) { foreach (var codeWithCounter in group) { sum += codeWithCounter.SumPerCounter[hardwareCounter]; } } return(sum); } ) }); } return(model); }
private string Export(Summary summary, BenchmarkCase benchmarkCase, DisassemblyResult disassemblyResult, PmcStats pmcStats) { string filePath = Path.Combine(summary.ResultsDirectoryPath, $"{FolderNameHelper.ToFolderName(benchmarkCase.Descriptor.Type)}." + $"{benchmarkCase.Descriptor.WorkloadMethod.Name}." + $"{GetShortRuntimeInfo(summary[benchmarkCase].GetRuntimeInfo())}.counters.html"); filePath.DeleteFileIfExists(); var totals = SumHardwareCountersStatsOfBenchmarkedCode(disassemblyResult, pmcStats); var perMethod = SumHardwareCountersPerMethod(disassemblyResult, pmcStats); using (var stream = new StreamWriter(filePath, append: false)) { using (var streamLogger = new StreamLogger(stream)) { Export(streamLogger, benchmarkCase, totals, perMethod, pmcStats.Counters.Keys.ToArray()); } } return(filePath); }