private static (int x, int y) PrintCode(string title, int x, int y, DecompiledMethod root) { XConsole.SetCursorPosition(x, y); XConsole.WriteLine(title); XConsole.DrawSeparator(10, x, y + 1); var codeLines = BuildCodeLines(root, out var refs); XConsole.SetCursorPosition(x + refs, y + 2); int newY = y + 2; int idx = 0; AssemblyInstruction prevInstruction = null; foreach (var assemblyCode in root.Instructions) { string prefix = null; bool isStartEnd = false; for (int r = refs - 1; r >= 0; r--) { if (assemblyCode.RefIds[r] > 0) { AssemblyInstruction nextInstruction = null; if (idx + 1 < root.Instructions.Count) { nextInstruction = root.Instructions[idx + 1]; } // // We are here since we have a jump overlap or a simple jump target // Check for jump overlaps. // // // Prev instruction has different ref inentifier so it means we have to end // our connection here. // if (prevInstruction != null && prevInstruction.RefIds[r] != assemblyCode.RefIds[r]) { prefix += XConsole.ConsoleBorderStyle.TopLeft.ToString(); prefix += XConsole.ConsoleBorderStyle.TopBottom.ToString(); isStartEnd = true; } // // Next instruction has a different ID we have to break the connection here. // else if (nextInstruction != null && nextInstruction.RefIds[r] != assemblyCode.RefIds[r]) { prefix += XConsole.ConsoleBorderStyle.BottomLeft.ToString(); prefix += XConsole.ConsoleBorderStyle.TopBottom.ToString(); isStartEnd = true; } else if (idx + 1 >= root.Instructions.Count) { prefix += XConsole.ConsoleBorderStyle.BottomLeft.ToString(); prefix += XConsole.ConsoleBorderStyle.TopBottom.ToString(); } // // Contrinue the line. // else { if (isStartEnd) { prefix += XConsole.ConsoleBorderStyle.SeparatorBoth.ToString(); prefix += XConsole.ConsoleBorderStyle.TopBottom.ToString(); } else { prefix += XConsole.ConsoleBorderStyle.Left.ToString() + " "; } } } // // We have a start instruction and we have the id so lets draw the line out // and go up or down. // // ------- // else if (isStartEnd) { prefix += XConsole.ConsoleBorderStyle.TopBottom.ToString(); prefix += XConsole.ConsoleBorderStyle.TopBottom.ToString(); } else { prefix += " "; } } codeLines[idx] = "`" + prefix + "`" + codeLines[idx]; XConsole.SetCursorPosition(x, newY); XConsole.WriteLine(codeLines[idx]); newY++; idx++; prevInstruction = assemblyCode; } return(XConsole.CursorLeft, XConsole.CursorTop); }
public static double Measure(Action a, BenchmarkOptions options = default(BenchmarkOptions)) { if (options == default(BenchmarkOptions)) { options = BenchmarkOptions.Default(); } var decompiler = new JitCodeDecompiler(); var warmupCount = options.WarmUpCount; var runCount = options.RunCount; DecompiledMethod before = null; if (options.Decompile) { a(); before = decompiler.DecompileMethod(a.Method, options.DecompileMethodName); } // // Quick Warm Up. // XConsole.WriteLine("Quick Benchmark:"); XConsole.WriteLine($" [1] Warm Up ...({warmupCount})"); for (int i = 0; i < warmupCount; i++) { a(); } Span <double> res = stackalloc double[runCount]; var initialMemory = GC.GetTotalAllocatedBytes(); XConsole.WriteLine($" [2] Running ...({runCount})"); for (int i = 0; i < runCount; i++) { Stopwatch w = new Stopwatch(); w.Start(); { a(); } w.Stop(); res[i] = w.ElapsedMilliseconds; } var memoryAfterRuns = GC.GetTotalAllocatedBytes(); double sum = 0; for (int i = 0; i < res.Length; i++) { XConsole.WriteLine($" [3-{i}] Took: `{res[i]}` ms"); sum += res[i]; } var cursonLeft = XConsole.CursorLeft; var mean = sum / runCount; var stdDev = StdDev(res, mean); var totalMemory = memoryAfterRuns - initialMemory; int separatorLen = cursonLeft + 30 + (runCount * 4); XConsole.DrawSeparator(separatorLen); XConsole.WriteLine($" Mean -> `{string.Format("{0:N4}", mean)}` ms"); XConsole.WriteLine($" StdDev -> `{string.Format("{0:N4}", stdDev)}` ms"); XConsole.WriteLine($" Total Memory -> `{totalMemory}` b\r\n"); var cursorTop = XConsole.CursorTop; XConsole.DrawPlot(30, cursorTop - 5, res.ToArray()); if (options.Decompile) { XConsole.WriteLine($" Decompiling Method: `{options.DecompileMethodName}`"); cursorTop += 2; var after = decompiler.DecompileMethod(before.CodeAddress, before.CodeSize, options.DecompileMethodName); if (before.CodeAddress == after.CodeAddress) { XConsole.WriteLine("Captured Only Single Tier"); var coords1 = PrintCode("Tier0", 1, cursorTop + 1, before); XConsole.SetCursorPosition(0, coords1.y); } else { var coords1 = PrintCode("Tier0", 1, cursorTop, before); var coords2 = PrintCode("Tier1", 50, cursorTop, after); int top = coords1.y > coords2.y ? coords1.y : coords2.y; XConsole.SetCursorPosition(0, top); } } return(mean); }