Skip to content

Instantly share code, notes, and snippets.

@DeCarabas
Created December 20, 2017 16:24
Show Gist options
  • Save DeCarabas/9a780a5e2d77d4dd84228c300bd0981a to your computer and use it in GitHub Desktop.
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#.
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