From e02e719ea56bf4ab2ed03e5cc29219614565620a Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sat, 23 Nov 2019 16:21:03 -0600 Subject: [PATCH] Add crypto benchmarks --- src/hactoolnet/MultiBenchmark.cs | 75 ++++++++++++++++++++++++++++++++ src/hactoolnet/ProcessBench.cs | 61 ++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 src/hactoolnet/MultiBenchmark.cs diff --git a/src/hactoolnet/MultiBenchmark.cs b/src/hactoolnet/MultiBenchmark.cs new file mode 100644 index 00000000..afa8aa51 --- /dev/null +++ b/src/hactoolnet/MultiBenchmark.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace hactoolnet +{ + internal class MultiBenchmark + { + public int RunsNeeded { get; set; } = 500; + + private List Benchmarks { get; } = new List(); + + public void Register(string name, Action setupAction, Action runAction, Func resultPrinter) + { + var benchmark = new BenchmarkItem + { + Name = name, + Setup = setupAction, + Run = runAction, + PrintResult = resultPrinter + }; + + Benchmarks.Add(benchmark); + } + + public void Run() + { + foreach (BenchmarkItem item in Benchmarks) + { + RunBenchmark(item); + + Console.WriteLine($"{item.Name}: {item.Result}"); + } + } + + private void RunBenchmark(BenchmarkItem item) + { + double fastestRun = double.MaxValue; + var watch = new Stopwatch(); + + int runsSinceLastBest = 0; + + while (runsSinceLastBest < RunsNeeded) + { + runsSinceLastBest++; + item.Setup(); + + watch.Restart(); + item.Run(); + watch.Stop(); + + if (fastestRun > watch.Elapsed.TotalSeconds) + { + fastestRun = watch.Elapsed.TotalSeconds; + + runsSinceLastBest = 0; + } + } + + item.Time = fastestRun; + item.Result = item.PrintResult(item.Time); + } + + private class BenchmarkItem + { + public string Name { get; set; } + public double Time { get; set; } + public string Result { get; set; } + + public Action Setup { get; set; } + public Action Run { get; set; } + public Func PrintResult { get; set; } + } + } +} diff --git a/src/hactoolnet/ProcessBench.cs b/src/hactoolnet/ProcessBench.cs index 9ab0228c..c5cab16d 100644 --- a/src/hactoolnet/ProcessBench.cs +++ b/src/hactoolnet/ProcessBench.cs @@ -15,6 +15,8 @@ namespace hactoolnet private const int BlockSizeBlocked = 0x10; private const int BlockSizeSeparate = 0x10; + private const int BatchCipherBenchSize = 1024 * 1024; + private static void CopyBenchmark(IStorage src, IStorage dst, int iterations, string label, IProgressReport logger) { // Warmup @@ -171,6 +173,53 @@ namespace hactoolnet logger.LogMessage($"{label}{averageRate}/s, fastest run: {fastestRate}/s, slowest run: {slowestRate}/s"); } + private static void RegisterAllCipherBenchmarks(MultiBenchmark bench) + { + var input = new byte[BatchCipherBenchSize]; + var output = new byte[BatchCipherBenchSize]; + + Func resultPrinter = time => Util.GetBytesReadable((long)(BatchCipherBenchSize / time)) + "/s"; + + // Skip the first benchmark set if we don't have AES-NI intrinsics + for (int i = Aes.IsAesNiSupported() ? 0 : 1; i < 2; i++) + { + // Prefer .NET crypto on the second set + string nameSuffix = i == 1 ? "built-in " : string.Empty; + bool preferDotNetImpl = i == 1; + + RegisterCipher($"AES-ECB {nameSuffix}encrypt", + () => Aes.CreateEcbEncryptor(new byte[0x10], preferDotNetImpl)); + + RegisterCipher($"AES-ECB {nameSuffix}decrypt", + () => Aes.CreateEcbDecryptor(new byte[0x10], preferDotNetImpl)); + + RegisterCipher($"AES-CBC {nameSuffix}encrypt", + () => Aes.CreateCbcEncryptor(new byte[0x10], new byte[0x10], preferDotNetImpl)); + + RegisterCipher($"AES-CBC {nameSuffix}decrypt", + () => Aes.CreateCbcDecryptor(new byte[0x10], new byte[0x10], preferDotNetImpl)); + + RegisterCipher($"AES-CTR {nameSuffix}decrypt", + () => Aes.CreateCtrDecryptor(new byte[0x10], new byte[0x10], preferDotNetImpl)); + + RegisterCipher($"AES-XTS {nameSuffix}encrypt", + () => Aes.CreateXtsEncryptor(new byte[0x10], new byte[0x10], new byte[0x10], preferDotNetImpl)); + + RegisterCipher($"AES-XTS {nameSuffix}decrypt", + () => Aes.CreateXtsDecryptor(new byte[0x10], new byte[0x10], new byte[0x10], preferDotNetImpl)); + } + + void RegisterCipher(string name, Func cipherGenerator) + { + ICipher cipher = null; + + Action setup = () => cipher = cipherGenerator(); + Action action = () => cipher.Transform(input, output); + + bench.Register(name, setup, action, resultPrinter); + } + } + private static void RunCipherBenchmark(Func cipherNet, Func cipherLibHac, CipherTaskSeparate function, bool benchBlocked, string label, IProgressReport logger) { @@ -295,6 +344,7 @@ namespace hactoolnet break; } + case "aescbcnew": { Func encryptorNet = () => Aes.CreateCbcEncryptor(new byte[0x10], new byte[0x10], true); @@ -325,6 +375,7 @@ namespace hactoolnet break; } + case "aesxtsnew": { Func encryptorNet = () => Aes.CreateXtsEncryptor(new byte[0x10], new byte[0x10], new byte[0x10], true); @@ -344,6 +395,16 @@ namespace hactoolnet break; } + case "crypto": + { + var bench = new MultiBenchmark(); + + RegisterAllCipherBenchmarks(bench); + + bench.Run(); + break; + } + default: ctx.Logger.LogMessage("Unknown benchmark type."); return;