Skip to content

Commit 1373da0

Browse files
committed
Change SpecCpuExecutor to parse metrics from CSV results.
1 parent 4ff0499 commit 1373da0

8 files changed

Lines changed: 1463 additions & 213 deletions

File tree

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.1.59
1+
2.1.60

src/VirtualClient/VirtualClient.Actions.UnitTests/Examples/SpecCpu/SpecCpuFpRateBaseExample.csv

Lines changed: 741 additions & 0 deletions
Large diffs are not rendered by default.

src/VirtualClient/VirtualClient.Actions.UnitTests/SPEC/SpecCpuMetricsParserTests.cs

Lines changed: 305 additions & 146 deletions
Large diffs are not rendered by default.

src/VirtualClient/VirtualClient.Actions/SPECcpu/SpecCpuExecutor.cs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ namespace VirtualClient.Actions
1919
using global::VirtualClient.Contracts;
2020
using Microsoft.Extensions.DependencyInjection;
2121
using VirtualClient.Contracts.Metadata;
22-
using VirtualClient.Metadata;
2322

2423
/// <summary>
2524
/// The SpecCpu workload executor.
@@ -145,6 +144,19 @@ public int Copies
145144
}
146145
}
147146

147+
/// <summary>
148+
/// True to parse the CSV file results vs. the standard output. The CSV results have finer-grained
149+
/// results based on significant figures.
150+
/// </summary>
151+
public string FeatureFlag
152+
{
153+
get
154+
{
155+
this.Parameters.TryGetValue(nameof(this.FeatureFlag), out IConvertible featureFlag);
156+
return featureFlag?.ToString();
157+
}
158+
}
159+
148160
/// <summary>
149161
/// The path to the SPECcpu package.
150162
/// </summary>
@@ -340,14 +352,25 @@ private async Task CaptureMetricsAsync(IProcessProxy process, string commandArgu
340352

341353
// CPU2017.008.intrate.txt
342354
string resultsDirectory = this.PlatformSpecifics.Combine(this.PackageDirectory, "result");
343-
string[] outputFiles = this.fileSystem.Directory.GetFiles(resultsDirectory, "CPU2017.*.txt", SearchOption.TopDirectoryOnly);
355+
string[] outputFiles = null;
356+
bool useCsv = false;
357+
358+
if (string.Equals(this.FeatureFlag, "UseCsvResults", StringComparison.OrdinalIgnoreCase))
359+
{
360+
outputFiles = this.fileSystem.Directory.GetFiles(resultsDirectory, "CPU2017.*.csv", SearchOption.TopDirectoryOnly);
361+
useCsv = true;
362+
}
363+
else
364+
{
365+
outputFiles = this.fileSystem.Directory.GetFiles(resultsDirectory, "CPU2017.*.txt", SearchOption.TopDirectoryOnly);
366+
}
344367

345368
foreach (string file in outputFiles)
346369
{
347370
KeyValuePair<string, string> results = await this.LoadResultsAsync(file, cancellationToken);
348371
await this.LogProcessDetailsAsync(process, telemetryContext, "SPECcpu", logToFile: true, results: results);
349372

350-
SpecCpuMetricsParser parser = new SpecCpuMetricsParser(results.Value);
373+
SpecCpuMetricsParser parser = new SpecCpuMetricsParser(results.Value, csv: useCsv);
351374
IList<Metric> metrics = parser.Parse();
352375
metrics.LogConsole(this.Scenario, "SPECcpu");
353376

@@ -392,7 +415,10 @@ private async Task UploadSpecCpuLogsAsync(CancellationToken cancellationToken)
392415
null,
393416
this.Roles?.FirstOrDefault())));
394417

395-
await this.UploadFilesAsync(blobManager, this.fileSystem, descriptors, cancellationToken);
418+
foreach (FileUploadDescriptor descriptor in descriptors)
419+
{
420+
await this.RequestFileUploadAsync(descriptor);
421+
}
396422
}
397423
}
398424
}

src/VirtualClient/VirtualClient.Actions/SPECcpu/SpecCpuMetricsParser.cs

Lines changed: 237 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,17 @@ public class SpecCpuMetricsParser : MetricsParser
2121
/// Separate the column values by 2 or more spaces.
2222
/// </summary>
2323
private static readonly Regex SpecCpuDataTableDelimiter = new Regex(@"(\s){2,}", RegexOptions.ExplicitCapture);
24-
25-
private List<Metric> metrics = new List<Metric>();
24+
private bool isCsv;
2625

2726
/// <summary>
2827
/// Constructor for <see cref="SpecCpuMetricsParser"/>
2928
/// </summary>
3029
/// <param name="rawText">Raw text to parse.</param>
31-
public SpecCpuMetricsParser(string rawText)
30+
/// <param name="csv">The content is from the SPEC CSV results file.</param>
31+
public SpecCpuMetricsParser(string rawText, bool csv = false)
3232
: base(rawText)
3333
{
34+
this.isCsv = csv;
3435
}
3536

3637
/// <summary>
@@ -59,30 +60,29 @@ public override IList<Metric> Parse()
5960
{
6061
try
6162
{
62-
this.Preprocess();
63-
this.ParseSpecCpuResult();
64-
this.ParseSpecCpuSummaryResult();
65-
66-
this.metrics.AddRange(this.SpecCpu.GetMetrics(nameIndex: 0, valueIndex: 3, unit: "Score", namePrefix: "SPECcpu-base-", metricRelativity: MetricRelativity.HigherIsBetter));
67-
this.metrics.AddRange(this.SpecCpu.GetMetrics(nameIndex: 0, valueIndex: 7, unit: "Score", namePrefix: "SPECcpu-peak-", ignoreFormatError: true, metricRelativity: MetricRelativity.HigherIsBetter));
68-
this.metrics.AddRange(this.SpecCpuSummary.GetMetrics(nameIndex: 0, valueIndex: 1, unit: "Score", namePrefix: string.Empty, ignoreFormatError: true, metricRelativity: MetricRelativity.HigherIsBetter));
69-
70-
// Every score in SPECcpu is critical metric.
71-
this.metrics.ForEach(m => m.Verbosity = 1);
63+
IEnumerable<Metric> metrics = null;
64+
if (this.isCsv)
65+
{
66+
metrics = this.ParseMetricsFromCsv();
67+
}
68+
else
69+
{
70+
metrics = this.ParseMetricsFromStandardOutput();
71+
}
7272

73-
return this.metrics;
73+
return metrics?.OrderBy(m => m.Name).ToList();
7474
}
7575
catch (Exception exc)
7676
{
77-
throw new WorkloadResultsException("Failed to parse SPECcpu metrics from results.", exc, ErrorReason.InvalidResults);
77+
throw new WorkloadResultsException("Failed to parse SPEC CPU benchmark metrics from results.", exc, ErrorReason.InvalidResults);
7878
}
7979
}
8080

8181
/// <inheritdoc/>
8282
protected override void Preprocess()
8383
{
8484
/*
85-
* Only capture data in selected test section, which is
85+
Only capture data in selected test section, which is
8686
=================================================================================
8787
500.perlbench_r 256 757 538 * 256 694 587 *
8888
502.gcc_r 256 634 572 * 256 511 710 *
@@ -139,5 +139,226 @@ private void ParseSpecCpuSummaryResult()
139139
this.SpecCpuSummary = DataTableExtensions.ConvertToDataTable(
140140
this.Sections[nameof(this.SpecCpuSummary)], SpecCpuMetricsParser.SpecCpuDataTableDelimiter, nameof(this.SpecCpuSummary), columnNames);
141141
}
142+
143+
private IEnumerable<Metric> ParseMetricsFromCsv()
144+
{
145+
List<Metric> metrics = new List<Metric>();
146+
147+
/*
148+
Only capture data in selected test section, which is
149+
=================================================================================
150+
"Selected Results Table"
151+
152+
Benchmark,"Base # Copies","Est. Base Run Time","Est. Base Rate","Base Selected","Base Status","Peak # Copies","Est. Peak Run Time","Est. Peak Rate","Peak Selected","Peak Status",Description
153+
503.bwaves_r,8,774.54528,103.575608,1,S,,,,,NR,"SelectedIteration (base #2; peak NR)"
154+
507.cactuBSSN_r,8,529.532167,19.12632,1,S,,,,,NR,"SelectedIteration (base #1; peak NR)"
155+
508.namd_r,8,355.876987,21.355696,1,S,,,,,NR,"SelectedIteration (base #2; peak NR)"
156+
510.parest_r,8,855.489505,24.463184,1,S,,,,,NR,"SelectedIteration (base #1; peak NR)"
157+
511.povray_r,8,777.63715,24.021488,1,S,,,,,NR,"SelectedIteration (base #1; peak NR)"
158+
519.lbm_r,8,613.604624,13.741744,1,S,,,,,NR,"SelectedIteration (base #2; peak NR)"
159+
521.wrf_r,8,575.203376,31.1542,1,S,,,,,NR,"SelectedIteration (base #1; peak NR)"
160+
526.blender_r,8,543.972241,22.3982,1,S,,,,,NR,"SelectedIteration (base #1; peak NR)"
161+
527.cam4_r,8,444.302751,31.49204,1,S,,,,,NR,"SelectedIteration (base #1; peak NR)"
162+
538.imagick_r,8,731.398457,27.20268,1,S,,,,,NR,"SelectedIteration (base #1; peak NR)"
163+
544.nab_r,8,488.412998,27.566832,1,S,,,,,NR,"SelectedIteration (base #2; peak NR)"
164+
549.fotonik3d_r,8,940.704803,33.141112,1,S,,,,,NR,"SelectedIteration (base #1; peak NR)"
165+
554.roms_r,8,609.94932,20.841072,1,S,,,,,NR,"SelectedIteration (base #2; peak NR)"
166+
167+
SPECrate2017_fp_base,26.914269,,26.914269
168+
SPECrate2017_fp_peak,"Not Run",,,,,,,"Not Run"
169+
*/
170+
171+
Match resultsSection = Regex.Match(this.RawText, "\"Selected Results Table\"[\\s\\S]+?SPEC(?:rate|speed)2017_(?:fp|int)_peak.*", RegexOptions.IgnoreCase);
172+
if (!resultsSection.Success)
173+
{
174+
throw new SchemaException($"Invalid results. SPEC CPU benchmark outcomes/information cannot be found in the results provided.");
175+
}
176+
177+
IEnumerable<string> results = Regex.Split(resultsSection.Groups[0].Value, "[\r\n]").Where(l => !string.IsNullOrWhiteSpace(l));
178+
foreach (string line in results)
179+
{
180+
string[] fields = line.Split(',', StringSplitOptions.TrimEntries);
181+
if (fields.Length > 2)
182+
{
183+
if (fields[0].StartsWith("Benchmark"))
184+
{
185+
continue;
186+
}
187+
188+
if (this.TryParseBaseMetric(fields, out Metric baseMetric))
189+
{
190+
metrics.Add(baseMetric);
191+
}
192+
193+
if (this.TryParsePeakMetric(fields, out Metric peakMetric))
194+
{
195+
metrics.Add(peakMetric);
196+
}
197+
198+
if (this.TryParseSummaryMetric(fields, out Metric summaryMetric))
199+
{
200+
metrics.Add(summaryMetric);
201+
}
202+
}
203+
}
204+
205+
return metrics;
206+
}
207+
208+
private IEnumerable<Metric> ParseMetricsFromStandardOutput()
209+
{
210+
List<Metric> metrics = new List<Metric>();
211+
212+
this.Preprocess();
213+
this.ParseSpecCpuResult();
214+
this.ParseSpecCpuSummaryResult();
215+
216+
metrics.AddRange(this.SpecCpu.GetMetrics(nameIndex: 0, valueIndex: 3, unit: "score", namePrefix: "SPECcpu-base-", metricRelativity: MetricRelativity.HigherIsBetter));
217+
metrics.AddRange(this.SpecCpu.GetMetrics(nameIndex: 0, valueIndex: 7, unit: "score", namePrefix: "SPECcpu-peak-", ignoreFormatError: true, metricRelativity: MetricRelativity.HigherIsBetter));
218+
metrics.AddRange(this.SpecCpuSummary.GetMetrics(nameIndex: 0, valueIndex: 1, unit: "score", namePrefix: string.Empty, ignoreFormatError: true, metricRelativity: MetricRelativity.HigherIsBetter));
219+
220+
// Every score in SPECcpu is critical metric.
221+
metrics.ForEach(m => m.Verbosity = 0);
222+
223+
return metrics;
224+
}
225+
226+
private bool TryParseBaseMetric(string[] fields, out Metric metric)
227+
{
228+
/*
229+
"Selected Results Table"
230+
231+
Benchmark,"Base # Copies","Est. Base Run Time","Est. Base Rate","Base Selected","Base Status","Peak # Copies","Est. Peak Run Time","Est. Peak Rate","Peak Selected","Peak Status",Description
232+
503.bwaves_r,8,774.54528,103.575608,1,S,,,,,NR,"SelectedIteration (base #2; peak NR)"
233+
507.cactuBSSN_r,8,529.532167,19.12632,1,S,,,,,NR,"SelectedIteration (base #1; peak NR)"
234+
*/
235+
236+
metric = null;
237+
238+
// Benchmark
239+
string benchmark = fields[0].Trim();
240+
241+
if (!Regex.IsMatch(benchmark, "^SPECrate|SPECspeed", RegexOptions.IgnoreCase))
242+
{
243+
// Est. Base Rate
244+
if (double.TryParse(fields[3], out double baseScore))
245+
{
246+
// Base # Copies
247+
double.TryParse(fields[1], out double baseCopies);
248+
249+
// Est. Base Run Time
250+
double.TryParse(fields[2], out double baseRunTime);
251+
252+
metric = new Metric(
253+
$"SPECcpu-base-{benchmark}",
254+
baseScore,
255+
unit: "score",
256+
relativity: MetricRelativity.HigherIsBetter,
257+
description: $"SPEC CPU '{benchmark}' benchmark base score.",
258+
metadata: new Dictionary<string, IConvertible>
259+
{
260+
{ "benchmark", benchmark },
261+
{ "numCopies", baseCopies },
262+
{ "runTime", baseRunTime }
263+
});
264+
265+
metric.Verbosity = 0;
266+
}
267+
}
268+
269+
return metric != null;
270+
}
271+
272+
private bool TryParsePeakMetric(string[] fields, out Metric metric)
273+
{
274+
/*
275+
"Selected Results Table"
276+
277+
Benchmark,"Base # Copies","Base Run Time","Base Rate","Base Selected","Base Status","Peak # Copies","Peak Run Time","Peak Rate","Peak Selected","Peak Status",Description
278+
503.bwaves_r,128,1649.037039,778.384,1,S,128,1649.037039,778.384,1,S,"SelectedIteration (base #1; peak #1)"
279+
507.cactuBSSN_r,128,221.93356,730.16448,1,S,128,220.578603,734.649728,1,S,"SelectedIteration (base #3; peak #3)"
280+
*/
281+
282+
metric = null;
283+
284+
// Benchmark
285+
string benchmark = fields[0].Trim();
286+
287+
if (!Regex.IsMatch(benchmark, "^SPECrate|SPECspeed", RegexOptions.IgnoreCase))
288+
{
289+
// Peak Rate
290+
if (double.TryParse(fields[8], out double peakScore))
291+
{
292+
// Peak # Copies
293+
double.TryParse(fields[6], out double peakCopies);
294+
295+
// Peak Run Time
296+
double.TryParse(fields[7], out double peakRunTime);
297+
298+
metric = new Metric(
299+
$"SPECcpu-peak-{benchmark}",
300+
peakScore,
301+
unit: "score",
302+
relativity: MetricRelativity.HigherIsBetter,
303+
description: $"SPEC CPU '{benchmark}' benchmark peak score.",
304+
metadata: new Dictionary<string, IConvertible>
305+
{
306+
{ "benchmark", benchmark },
307+
{ "numCopies", peakCopies },
308+
{ "runTime", peakRunTime }
309+
});
310+
311+
metric.Verbosity = 0;
312+
}
313+
}
314+
315+
return metric != null;
316+
}
317+
318+
private bool TryParseSummaryMetric(string[] fields, out Metric metric)
319+
{
320+
/*
321+
"Selected Results Table"
322+
323+
Benchmark,"Base # Copies","Base Run Time","Base Rate","Base Selected","Base Status","Peak # Copies","Peak Run Time","Peak Rate","Peak Selected","Peak Status",Description
324+
...
325+
...
326+
SPECspeed2017_int_base,12.279658,,12.279658
327+
SPECspeed2017_int_peak,12.293316,,,,,,,12.293316
328+
*/
329+
330+
metric = null;
331+
332+
// Benchmark
333+
string benchmark = fields[0].Trim();
334+
335+
IDictionary<string, string> metricMapping = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
336+
{
337+
{ "SPECrate2017_fp_base", "SPECrate(R)2017_fp_base,SPEC CPU floating point base rate summary score." },
338+
{ "SPECrate2017_fp_peak", "SPECrate(R)2017_fp_peak,SPEC CPU floating point peak rate summary score." },
339+
{ "SPECrate2017_int_base", "SPECrate(R)2017_int_base,SPEC CPU integer base rate summary score." },
340+
{ "SPECrate2017_int_peak", "SPECrate(R)2017_int_peak,SPEC CPU integer peak rate summary score." },
341+
{ "SPECspeed2017_fp_base", "SPECspeed(R)2017_fp_base,SPEC CPU floating point base speed summary score." },
342+
{ "SPECspeed2017_fp_peak", "SPECspeed(R)2017_fp_peak,SPEC CPU floating point peak speed summary score." },
343+
{ "SPECspeed2017_int_base", "SPECspeed(R)2017_int_base,SPEC CPU integer base speed summary score." },
344+
{ "SPECspeed2017_int_peak", "SPECspeed(R)2017_int_peak,SPEC CPU integer peak speed summary score." }
345+
};
346+
347+
if (metricMapping.TryGetValue(benchmark, out string metricDescription) && double.TryParse(fields[1], out double score))
348+
{
349+
string[] metricDetails = metricDescription.Split(',');
350+
351+
metric = new Metric(
352+
metricDetails[0],
353+
score,
354+
unit: "score",
355+
relativity: MetricRelativity.HigherIsBetter,
356+
description: metricDetails[1]);
357+
358+
metric.Verbosity = 0;
359+
}
360+
361+
return metric != null;
362+
}
142363
}
143364
}

0 commit comments

Comments
 (0)