Created
December 20, 2017 16:24
-
-
Save DeCarabas/9a780a5e2d77d4dd84228c300bd0981a to your computer and use it in GitHub Desktop.
A performance tool I built based on Gary Bernhardt's ReadyGo, but in C#.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace jsonperf | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Threading.Tasks; | |
using System.IO; | |
using Newtonsoft.Json; | |
using Newtonsoft.Json.Linq; | |
using System.Runtime.CompilerServices; | |
using System.Diagnostics; | |
using StoreDiagnostics.Services; | |
static class Benchmark | |
{ | |
static StreamReader streamReader; | |
public static void Setup() | |
{ | |
streamReader = new StreamReader(@"minecraft.json"); | |
} | |
public static void Cleanup() | |
{ | |
streamReader.Close(); | |
} | |
[MethodImpl(MethodImplOptions.NoInlining)] | |
public static void Go() | |
{ | |
streamReader.BaseStream.Position = 0; | |
//JToken.ReadFrom(new JsonTextReader(streamReader)) | |
// ["Product"]["LocalizedProperties"][0]["ProductTitle"].Value<string>(); | |
var x = new StreamingJsonReader(streamReader); | |
x.SkipToValueAsync("Product", "LocalizedProperties", 0, "ProductTitle").Wait(); | |
string s = (string)x.Value; | |
} | |
} | |
static class NullBenchmark | |
{ | |
[MethodImpl(MethodImplOptions.NoInlining)] | |
public static void Go() { } | |
} | |
public class Program | |
{ | |
const int OuterIterations = 16; | |
static readonly TimeSpan MinimumTime = TimeSpan.FromMilliseconds(3); | |
static TimeSpan MeasureRuntime(int iterations, Action action) | |
{ | |
var sw = new Stopwatch(); | |
GC.Collect(); | |
sw.Start(); | |
for (int i = 0; i < iterations; i++) | |
{ | |
action(); | |
} | |
sw.Stop(); | |
return sw.Elapsed; | |
} | |
static TimeSpan RunBenchmark(int iterations) | |
{ | |
TimeSpan rawTime = MeasureRuntime(iterations, () => Benchmark.Go()); | |
TimeSpan constantTime = MeasureRuntime(iterations, () => NullBenchmark.Go()); | |
return rawTime - constantTime; | |
} | |
static void Prime() | |
{ | |
Benchmark.Setup(); | |
Benchmark.Go(); | |
Benchmark.Cleanup(); | |
} | |
static TimeSpan CaptureTime(int iterations, bool throwOnTooFast = false) | |
{ | |
Benchmark.Setup(); | |
TimeSpan result = RunBenchmark(iterations); | |
Benchmark.Cleanup(); | |
if (throwOnTooFast) | |
{ | |
if (result < MinimumTime) { throw new BenchmarkTooFastException(); } | |
} | |
return TimeSpan.FromMilliseconds(result.TotalMilliseconds / iterations); | |
} | |
static TimeSpan Percentile(TimeSpan[] times, int percentile) | |
{ | |
TimeSpan[] sortedTimes = new TimeSpan[times.Length]; | |
Array.Copy(times, sortedTimes, times.Length); | |
Array.Sort(sortedTimes); | |
double ratio = percentile * 0.01; | |
double h = ratio * ((double)(sortedTimes.Length - 1)) + 1.0; | |
int k = (int)(Math.Floor(h) - 1.0); | |
double f = h % 1.0; | |
return TimeSpan.FromMilliseconds(sortedTimes[k].TotalMilliseconds + | |
(f * (sortedTimes[k + 1].TotalMilliseconds - sortedTimes[k].TotalMilliseconds))); | |
} | |
public void Main(string[] args) | |
{ | |
TimeSpan[] times = new TimeSpan[OuterIterations]; | |
Prime(); | |
int iterations = 1; | |
while (true) | |
{ | |
try | |
{ | |
times[0] = CaptureTime(iterations, throwOnTooFast: true); | |
break; | |
} | |
catch (BenchmarkTooFastException) | |
{ | |
Console.Write("!"); | |
iterations *= 2; | |
continue; | |
} | |
} | |
Console.Write("."); | |
for (int i = 1; i < OuterIterations; i++) | |
{ | |
times[i] = CaptureTime(iterations); | |
Console.Write("."); | |
} | |
Console.WriteLine(); | |
Array.Sort(times); | |
Console.WriteLine("Min time: {0} (80th: {1})", times[0], Percentile(times, 80)); | |
} | |
class BenchmarkTooFastException : Exception { } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment