Skip to content

Commit a11b49a

Browse files
authored
JsonExporter: make Json export more extensible. (#2081)
1 parent 52d7707 commit a11b49a

File tree

1 file changed

+57
-47
lines changed

1 file changed

+57
-47
lines changed

src/BenchmarkDotNet/Exporters/Json/JsonExporterBase.cs

Lines changed: 57 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using BenchmarkDotNet.Environments;
44
using BenchmarkDotNet.Loggers;
55
using BenchmarkDotNet.Reports;
6+
using Perfolizer.Horology;
67
using JsonSerializer = SimpleJson.SimpleJson;
78

89
namespace BenchmarkDotNet.Exporters.Json
@@ -21,53 +22,71 @@ protected JsonExporterBase(bool indentJson = false, bool excludeMeasurements = f
2122
}
2223

2324
public override void ExportToLog(Summary summary, ILogger logger)
25+
{
26+
JsonSerializer.CurrentJsonSerializerStrategy.Indent = IndentJson;
27+
logger.WriteLine(JsonSerializer.SerializeObject(GetDataToSerialize(summary)));
28+
}
29+
30+
protected virtual IReadOnlyDictionary<string, object> GetDataToSerialize(Summary summary)
31+
{
32+
// If we just ask SimpleJson to serialize the entire "summary" object it throws several errors.
33+
// So we are more specific in what we serialize (plus some fields/properties aren't relevant)
34+
return new Dictionary<string, object>
35+
{
36+
{ "Title", summary.Title },
37+
{ "HostEnvironmentInfo", GetDataToSerialize(summary.HostEnvironmentInfo) },
38+
{ "Benchmarks", summary.Reports.Select(GetDataToSerialize) }
39+
};
40+
}
41+
42+
protected virtual IReadOnlyDictionary<string, object> GetDataToSerialize(HostEnvironmentInfo environmentInfo)
2443
{
2544
// We construct HostEnvironmentInfo manually, so that we can have the HardwareTimerKind enum as text, rather than an integer
2645
// SimpleJson serializer doesn't seem to have an enum String/Value option (to-be-fair, it is meant to be "Simple")
27-
var environmentInfo = new
46+
return new Dictionary<string, object>
2847
{
29-
HostEnvironmentInfo.BenchmarkDotNetCaption,
30-
summary.HostEnvironmentInfo.BenchmarkDotNetVersion,
31-
OsVersion = summary.HostEnvironmentInfo.OsVersion.Value,
32-
ProcessorName = ProcessorBrandStringHelper.Prettify(summary.HostEnvironmentInfo.CpuInfo.Value),
33-
summary.HostEnvironmentInfo.CpuInfo.Value?.PhysicalProcessorCount,
34-
summary.HostEnvironmentInfo.CpuInfo.Value?.PhysicalCoreCount,
35-
summary.HostEnvironmentInfo.CpuInfo.Value?.LogicalCoreCount,
36-
summary.HostEnvironmentInfo.RuntimeVersion,
37-
summary.HostEnvironmentInfo.Architecture,
38-
summary.HostEnvironmentInfo.HasAttachedDebugger,
39-
summary.HostEnvironmentInfo.HasRyuJit,
40-
summary.HostEnvironmentInfo.Configuration,
41-
DotNetCliVersion = summary.HostEnvironmentInfo.DotNetSdkVersion.Value,
42-
summary.HostEnvironmentInfo.ChronometerFrequency,
43-
HardwareTimerKind = summary.HostEnvironmentInfo.HardwareTimerKind.ToString()
48+
{ nameof(HostEnvironmentInfo.BenchmarkDotNetCaption), HostEnvironmentInfo.BenchmarkDotNetCaption },
49+
{ nameof(environmentInfo.BenchmarkDotNetVersion), environmentInfo.BenchmarkDotNetVersion },
50+
{ "OsVersion", environmentInfo.OsVersion.Value },
51+
{ "ProcessorName", ProcessorBrandStringHelper.Prettify(environmentInfo.CpuInfo.Value) },
52+
{ "PhysicalProcessorCount", environmentInfo.CpuInfo.Value?.PhysicalProcessorCount },
53+
{ "PhysicalCoreCount", environmentInfo.CpuInfo.Value?.PhysicalCoreCount },
54+
{ "LogicalCoreCount", environmentInfo.CpuInfo.Value?.LogicalCoreCount },
55+
{ nameof(environmentInfo.RuntimeVersion), environmentInfo.RuntimeVersion },
56+
{ nameof(environmentInfo.Architecture), environmentInfo.Architecture },
57+
{ nameof(environmentInfo.HasAttachedDebugger), environmentInfo.HasAttachedDebugger },
58+
{ nameof(environmentInfo.HasRyuJit), environmentInfo.HasRyuJit },
59+
{ nameof(environmentInfo.Configuration), environmentInfo.Configuration },
60+
{ "DotNetCliVersion", environmentInfo.DotNetSdkVersion.Value },
61+
{ nameof(environmentInfo.ChronometerFrequency), environmentInfo.ChronometerFrequency },
62+
{ nameof(HardwareTimerKind), environmentInfo.HardwareTimerKind.ToString() },
4463
};
64+
}
4565

46-
// If we just ask SimpleJson to serialize the entire "summary" object it throws several errors.
47-
// So we are more specific in what we serialize (plus some fields/properties aren't relevant)
48-
49-
var benchmarks = summary.Reports.Select(report =>
66+
protected virtual IReadOnlyDictionary<string, object> GetDataToSerialize(BenchmarkReport report)
67+
{
68+
var benchmark = new Dictionary<string, object>
5069
{
51-
var data = new Dictionary<string, object>
70+
// We don't need Benchmark.ShortInfo, that info is available via Benchmark.Parameters below
71+
{ "DisplayInfo", report.BenchmarkCase.DisplayInfo },
72+
{ "Namespace", report.BenchmarkCase.Descriptor.Type.Namespace },
73+
{ "Type", FullNameProvider.GetTypeName(report.BenchmarkCase.Descriptor.Type) },
74+
{ "Method", report.BenchmarkCase.Descriptor.WorkloadMethod.Name },
75+
{ "MethodTitle", report.BenchmarkCase.Descriptor.WorkloadMethodDisplayInfo },
76+
{ "Parameters", report.BenchmarkCase.Parameters.PrintInfo },
5277
{
53-
// We don't need Benchmark.ShortInfo, that info is available via Benchmark.Parameters below
54-
{ "DisplayInfo", report.BenchmarkCase.DisplayInfo },
55-
{ "Namespace", report.BenchmarkCase.Descriptor.Type.Namespace },
56-
{ "Type", FullNameProvider.GetTypeName(report.BenchmarkCase.Descriptor.Type) },
57-
{ "Method", report.BenchmarkCase.Descriptor.WorkloadMethod.Name },
58-
{ "MethodTitle", report.BenchmarkCase.Descriptor.WorkloadMethodDisplayInfo },
59-
{ "Parameters", report.BenchmarkCase.Parameters.PrintInfo },
60-
{ "FullName", FullNameProvider.GetBenchmarkName(report.BenchmarkCase) }, // do NOT remove this property, it is used for xunit-performance migration
61-
// Hardware Intrinsics can be disabled using env vars, that is why they might be different per benchmark and are not exported as part of HostEnvironmentInfo
62-
{ "HardwareIntrinsics", report.GetHardwareIntrinsicsInfo() ?? "" },
63-
// { "Properties", r.Benchmark.Job.ToSet().ToDictionary(p => p.Name, p => p.Value) }, // TODO
64-
{ "Statistics", report.ResultStatistics }
65-
};
78+
"FullName", FullNameProvider.GetBenchmarkName(report.BenchmarkCase)
79+
}, // do NOT remove this property, it is used for xunit-performance migration
80+
// Hardware Intrinsics can be disabled using env vars, that is why they might be different per benchmark and are not exported as part of HostEnvironmentInfo
81+
{ "HardwareIntrinsics", report.GetHardwareIntrinsicsInfo() ?? "" },
82+
// { "Properties", r.Benchmark.Job.ToSet().ToDictionary(p => p.Name, p => p.Value) }, // TODO
83+
{ "Statistics", report.ResultStatistics }
84+
};
6685

6786
// We show MemoryDiagnoser's results only if it is being used
6887
if (report.BenchmarkCase.Config.HasMemoryDiagnoser())
6988
{
70-
data.Add("Memory", new
89+
benchmark.Add("Memory", new
7190
{
7291
report.GcStats.Gen0Collections,
7392
report.GcStats.Gen1Collections,
@@ -80,7 +99,7 @@ public override void ExportToLog(Summary summary, ILogger logger)
8099
if (ExcludeMeasurements == false)
81100
{
82101
// We construct Measurements manually, so that we can have the IterationMode enum as text, rather than an integer
83-
data.Add("Measurements",
102+
benchmark.Add("Measurements",
84103
report.AllMeasurements.Select(m => new
85104
{
86105
IterationMode = m.IterationMode.ToString(),
@@ -93,20 +112,11 @@ public override void ExportToLog(Summary summary, ILogger logger)
93112

94113
if (report.Metrics.Any())
95114
{
96-
data.Add("Metrics", report.Metrics.Values);
115+
benchmark.Add("Metrics", report.Metrics.Values);
97116
}
98117
}
99118

100-
return data;
101-
});
102-
103-
JsonSerializer.CurrentJsonSerializerStrategy.Indent = IndentJson;
104-
logger.WriteLine(JsonSerializer.SerializeObject(new Dictionary<string, object>
105-
{
106-
{ "Title", summary.Title },
107-
{ "HostEnvironmentInfo", environmentInfo },
108-
{ "Benchmarks", benchmarks }
109-
}));
119+
return benchmark;
110120
}
111121
}
112122
}

0 commit comments

Comments
 (0)