Skip to content

Commit 44dcd8a

Browse files
authored
Support ColumnChart OutputMode (#24)
1 parent 1089781 commit 44dcd8a

File tree

7 files changed

+179
-22
lines changed

7 files changed

+179
-22
lines changed

Benchly.Benchmarks/Md5VsSha256.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace Benchly.Benchmarks
66
{
77
[BoxPlot(Title = "Box Plot", Colors = "skyblue,slateblue", Height = 800)]
8-
[ColumnChart(Title = "Column Chart", Colors = "skyblue,slateblue", Height =800)]
8+
[ColumnChart(Title = "Column Chart", Colors = "skyblue,slateblue", Height =800, Output = OutputMode.PerJob)]
99
[Histogram(Width=500)]
1010
[Timeline(Width = 500)]
1111
[MemoryDiagnoser, SimpleJob(RuntimeMoniker.Net60), SimpleJob(RuntimeMoniker.Net48)]

Benchly.Benchmarks/Md5VsSha256Params.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace Benchly.Benchmarks
66
{
77
[BoxPlot(Title = "Box Plot")]
8-
[ColumnChart(Title = "Column Chart")]
8+
[ColumnChart(Title = "Column Chart", Output=OutputMode.Combined)]
99
[Histogram]
1010
[Timeline]
1111
[MemoryDiagnoser, SimpleJob(RuntimeMoniker.Net60), SimpleJob(RuntimeMoniker.Net48)]

Benchly.UnitTests/PlotInfoTests.cs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,38 @@
11
using FluentAssertions;
22
using Plotly.NET;
3-
using System;
4-
using System.Collections.Generic;
5-
using System.Linq;
6-
using System.Text;
7-
using System.Threading.Tasks;
83

94
namespace Benchly.UnitTests
105
{
116
public class PlotInfoTests
127
{
138
[Fact]
14-
public void WhenColorIsStringItIsMapped()
9+
public void WhenColorIsInvalidStringGetColorsIsEmpty()
1510
{
1611
var info = new PlotInfo();
1712
info.Colors = "foo";
1813

1914
var colors = info.GetColors();
2015

16+
colors.Should().NotBeNull();
17+
colors.Should().BeEmpty();
18+
}
19+
20+
[Fact]
21+
public void WhenColorIsValidStringGetColorsReturnsColor()
22+
{
23+
var info = new PlotInfo();
24+
info.Colors = "firebrick";
25+
26+
var colors = info.GetColors();
27+
2128
colors.Should().NotBeNull();
2229
colors.Length.Should().Be(1);
23-
colors[0].Value.Should().Be("foo");
30+
var argb = colors[0].Value.Should().BeOfType<ARGB>().Subject;
31+
32+
argb.A.Should().Be(255);
33+
argb.R.Should().Be(178);
34+
argb.G.Should().Be(34);
35+
argb.B.Should().Be(34);
2436
}
2537

2638
[Fact]

Benchly/BoxPlotAttribute.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,27 @@
33

44
namespace Benchly
55
{
6+
/// <summary>
7+
/// The chart output mode.
8+
/// </summary>
9+
public enum OutputMode
10+
{
11+
/// <summary>
12+
/// Output a chart per benchmark method.
13+
/// </summary>
14+
PerMethod,
15+
16+
/// <summary>
17+
/// Output a chart per benchmark job. This groups methods by job.
18+
/// </summary>
19+
PerJob,
20+
21+
/// <summary>
22+
/// Output a combined chart, showing all jobs and methods.
23+
/// </summary>
24+
Combined
25+
}
26+
627
/// <summary>
728
/// Export a box plot.
829
/// </summary>
@@ -60,6 +81,15 @@ public string Colors
6081
set => plotInfo.Colors = value;
6182
}
6283

84+
/// <summary>
85+
/// Gets or sets the output mode.
86+
/// </summary>
87+
public OutputMode Output
88+
{
89+
get => plotInfo.OutputMode;
90+
set => plotInfo.OutputMode = value;
91+
}
92+
6393
/// <summary>
6494
/// Initializes a new instance of the <see cref="ColumnChartAttribute"/> class.
6595
/// </summary>

Benchly/ColorMap.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@ namespace Benchly
55
{
66
internal class ColorMap
77
{
8+
private static Color[] defaults = new[] { Color.fromKeyword(ColorKeyword.IndianRed), Color.fromKeyword(ColorKeyword.Salmon) };
9+
10+
internal static Color[] GetColorList(PlotInfo info)
11+
{
12+
var colors = info.GetColors();
13+
14+
if (colors.Length == 0)
15+
{
16+
colors = defaults;
17+
}
18+
19+
return colors;
20+
}
21+
822
internal static Dictionary<string, Color> GetJobColors(Summary summary, PlotInfo info)
923
{
1024
var jobs = summary.Reports.Select(r => r.BenchmarkCase.Job.ResolvedId).Distinct().ToList();
@@ -16,7 +30,7 @@ internal static Dictionary<string, Color> GetJobColors(Summary summary, PlotInfo
1630
// give some default colors
1731
if (colors.Length == 0)
1832
{
19-
colors = new[] { Color.fromKeyword(ColorKeyword.IndianRed), Color.fromKeyword(ColorKeyword.Salmon) };
33+
colors = defaults;
2034
}
2135

2236
for (int i = 0; i < jobs.Count; i++)

Benchly/ColumnChartExporter.cs

Lines changed: 108 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using BenchmarkDotNet.Exporters;
2-
using BenchmarkDotNet.Reports;
3-
using Plotly.NET.ImageExport;
42
using BenchmarkDotNet.Loggers;
3+
using BenchmarkDotNet.Reports;
4+
55
using Plotly.NET;
6+
using Plotly.NET.ImageExport;
67
using Plotly.NET.LayoutObjects;
78
using static Plotly.NET.StyleParam;
9+
810
using Microsoft.FSharp.Core;
911

1012
namespace Benchly
@@ -22,24 +24,120 @@ public IEnumerable<string> ExportToFiles(Summary summary, ILogger consoleLogger)
2224
return Array.Empty<string>();
2325
}
2426

25-
if (summary.Reports[0].BenchmarkCase.HasParameters)
27+
return Info.OutputMode switch
2628
{
27-
int paramCount = summary.Reports[0].BenchmarkCase.Parameters.Count;
29+
OutputMode.PerMethod => PerMethod(summary),
30+
OutputMode.PerJob => PerJob(summary),
31+
OutputMode.Combined => Combined(summary),
32+
_ => Array.Empty<string>(),
33+
};
34+
}
2835

29-
if (paramCount == 1)
36+
public void ExportToLog(Summary summary, ILogger logger)
37+
{
38+
}
39+
40+
// Revisit this in terms of params:
41+
// This would make most sense if used with params, so that you
42+
// could look at the results for all param values for each method
43+
private IEnumerable<string> PerMethod(Summary summary)
44+
{
45+
var files = new List<string>();
46+
47+
foreach (var report in summary.Reports)
48+
{
49+
if (!report.Success)
3050
{
31-
return OneParameter(summary);
51+
continue;
3252
}
53+
54+
int paramCount = report.BenchmarkCase.Parameters.Count;
55+
56+
var title = this.Info.Title ?? summary.Title;
57+
var fileSlug = paramCount == 0
58+
? report.BenchmarkCase.Job.ResolvedId + "-" + report.BenchmarkCase.Descriptor.WorkloadMethodDisplayInfo
59+
: report.BenchmarkCase.Job.ResolvedId + "-" + report.BenchmarkCase.Descriptor.WorkloadMethodDisplayInfo + "-" + report.BenchmarkCase.Parameters.PrintInfo;
60+
61+
fileSlug += "-columnchart";
62+
var file = Path.Combine(summary.ResultsDirectoryPath, ExporterBase.GetFileName(summary) + fileSlug);
63+
64+
var mean = new[] { ConvertNanosToMs(report.ResultStatistics.Mean) };
65+
var name = report.BenchmarkCase.Descriptor.WorkloadMethodDisplayInfo;
66+
67+
Chart2D.Chart.Column<double, string, string, double, double>(mean, Name: name)
68+
.WithAxisTitles("Time (ms)")
69+
.WithoutVerticalGridlines()
70+
.WithLayout(title)
71+
.SaveSVG(file, Width: Info.Width, Height: Info.Height);
72+
73+
files.Add(file + ".svg");
74+
}
75+
return files;
76+
}
77+
private IEnumerable<string> PerJob(Summary summary)
78+
{
79+
// don't support params for now
80+
if (summary.Reports[0].BenchmarkCase.HasParameters)
81+
{
82+
return Array.Empty<string>();
83+
}
84+
85+
var files = new List<string>();
86+
87+
var jobs = summary.Reports.Select(r => new
88+
{
89+
job = r.BenchmarkCase.Job.ResolvedId,
90+
name = r.BenchmarkCase.Descriptor.WorkloadMethodDisplayInfo,
91+
mean = r.Success ? ConvertNanosToMs(r.ResultStatistics.Mean) : 0
92+
}).GroupBy(r => r.job);
93+
94+
foreach (var job in jobs)
95+
{
96+
var title = this.Info.Title ?? summary.Title;
97+
var file = Path.Combine(summary.ResultsDirectoryPath, ExporterBase.GetFileName(summary) + "-" + job.Key + "-columnchart");
98+
99+
var colors = ColorMap.GetColorList(Info);
100+
101+
// make 1 chart per column so that we can color by bar index. Legend is disabled since it is not needed.
102+
var charts = job
103+
.Select((j, i) => Chart2D.Chart.Column<double, string, string, double, double>(
104+
new[] { j.mean },
105+
new[] { j.name },
106+
Name: job.Key,
107+
MarkerColor: colors[i % colors.Length])
108+
.WithLegendGroup(job.Key, false));
109+
110+
Chart.Combine(charts)
111+
.WithAxisTitles("Time (ms)")
112+
.WithoutVerticalGridlines()
113+
.WithLayout(title)
114+
.SaveSVG(file, Width: Info.Width, Height: Info.Height);
115+
116+
files.Add(file + ".svg");
33117
}
34118

35-
return NoParameter(summary);
119+
return files;
36120
}
37121

38-
public void ExportToLog(Summary summary, ILogger logger)
122+
private IEnumerable<string> Combined(Summary summary)
39123
{
124+
if (summary.Reports[0].BenchmarkCase.HasParameters)
125+
{
126+
int paramCount = summary.Reports[0].BenchmarkCase.Parameters.Count;
127+
128+
if (paramCount == 1)
129+
{
130+
return OneParameterCombined(summary);
131+
}
132+
133+
// we only support 0 or 1 params
134+
return Array.Empty<string>();
135+
}
136+
137+
return NoParameterCombined(summary);
40138
}
41139

42-
private IEnumerable<string> NoParameter(Summary summary)
140+
private IEnumerable<string> NoParameterCombined(Summary summary)
43141
{
44142
var title = this.Info.Title ?? summary.Title;
45143
var file = Path.Combine(summary.ResultsDirectoryPath, ExporterBase.GetFileName(summary) + "-columnchart");
@@ -72,7 +170,7 @@ private IEnumerable<string> NoParameter(Summary summary)
72170
return new[] { file + ".svg" };
73171
}
74172

75-
private IEnumerable<string> OneParameter(Summary summary)
173+
private IEnumerable<string> OneParameterCombined(Summary summary)
76174
{
77175
var title = this.Info.Title ?? summary.Title;
78176
var file = Path.Combine(summary.ResultsDirectoryPath, ExporterBase.GetFileName(summary) + "-columnchart");
@@ -112,7 +210,6 @@ private IEnumerable<string> OneParameter(Summary summary)
112210
}
113211

114212
// https://github.com/plotly/Plotly.NET/issues/387
115-
// The alignment of this isn't 100% correct. Another approach may be to give each sub chart an x axis title
116213
double xWidth = 1.0d / byParam.Count();
117214
double xMidpoint = xWidth / 2.0d;
118215
double[] xs = byParam.Select((_, index) => xMidpoint + (xWidth * index)).ToArray();

Benchly/PlotInfo.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ internal class PlotInfo
1212

1313
public int Height { get; set; } = 600;
1414

15+
public OutputMode OutputMode { get; set; } = OutputMode.Combined;
16+
1517
internal Color[] GetColors()
1618
{
1719
if (string.IsNullOrEmpty(Colors))
@@ -38,7 +40,9 @@ private Color ParseColor(string x)
3840
return Color.fromHex(x);
3941
}
4042

41-
return Color.fromString(x);
43+
var keyword = ColorKeyword.ofKeyWord.Invoke(x);
44+
var c = Color.fromKeyword(keyword);
45+
return c;
4246
}
4347
}
4448
}

0 commit comments

Comments
 (0)