From 504ffb92380c5f1a26b714d1c081046e07637c3e Mon Sep 17 00:00:00 2001 From: Yang Pan <31965446+yangpanMS@users.noreply.github.com> Date: Thu, 5 Dec 2024 00:38:02 -0800 Subject: [PATCH] Change DiskSpd Parser to skip parsing read on write-only and vise-versa (#410) * Change DiskSpd Parser to skip parsing read on write-only and vise versa * default enum * test fix --- VERSION | 2 +- .../DiskSpd/DiskSpdMetricsParserTests.cs | 30 +---- .../DiskSpd/{Read8k.txt => Write8k.txt} | 0 .../DiskSpd/DiskSpdExecutor.cs | 2 +- .../DiskSpd/DiskSpdMetricsParser.cs | 127 +++++++++--------- .../DataTableExtensionsUnitTests.cs | 32 ----- 6 files changed, 76 insertions(+), 117 deletions(-) rename src/VirtualClient/VirtualClient.Actions.UnitTests/Examples/DiskSpd/{Read8k.txt => Write8k.txt} (100%) delete mode 100644 src/VirtualClient/VirtualClient.TestFramework.UnitTests/DataTableExtensionsUnitTests.cs diff --git a/VERSION b/VERSION index bac66dafe8..d3799fb250 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.16.9 +1.16.10 diff --git a/src/VirtualClient/VirtualClient.Actions.UnitTests/DiskSpd/DiskSpdMetricsParserTests.cs b/src/VirtualClient/VirtualClient.Actions.UnitTests/DiskSpd/DiskSpdMetricsParserTests.cs index 9bd97640f1..ebf4c28855 100644 --- a/src/VirtualClient/VirtualClient.Actions.UnitTests/DiskSpd/DiskSpdMetricsParserTests.cs +++ b/src/VirtualClient/VirtualClient.Actions.UnitTests/DiskSpd/DiskSpdMetricsParserTests.cs @@ -1,11 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + using System.IO; using System.Reflection; using NUnit.Framework; using System.Collections.Generic; using VirtualClient.Contracts; -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. namespace VirtualClient.Actions { @@ -17,7 +18,7 @@ public class DiskSpdMetricsParserTests public void DiskSpdParserVerifyReadWrite() { string results = File.ReadAllText(MockFixture.GetDirectory(typeof(DiskSpdMetricsParserTests), "Examples", "DiskSpd", "DiskSpdExample-ReadWrite.txt")); - var parser = new DiskSpdMetricsParser(results); + var parser = new DiskSpdMetricsParser(results, "diskspd.exe -b8K -r8K -t32 -o16 -w50 -d900 -Suw -W50 -D -L -Rtext D:\\diskspd-test.dat"); IList metrics = parser.Parse(); @@ -128,7 +129,7 @@ public void DiskSpdParserVerifyReadWrite() public void DiskSpdParserVerifyWriteOnly() { string results = File.ReadAllText(MockFixture.GetDirectory(typeof(DiskSpdMetricsParserTests), "Examples", "DiskSpd", "DiskSpdExample-WriteOnly.txt")); - var parser = new DiskSpdMetricsParser(results); + var parser = new DiskSpdMetricsParser(results, "diskspd.exe -b8K -r8K -t32 -o16 -w100 -d900 -Suw -W30 -D -L -Rtext D:\\diskspd-test.dat"); IList metrics = parser.Parse(); @@ -152,18 +153,6 @@ public void DiskSpdParserVerifyWriteOnly() MetricAssert.Exists(metrics, "total iops total", 76458.71, "iops"); MetricAssert.Exists(metrics, "total latency average total", 6.696, "ms"); - // Read - MetricAssert.Exists(metrics, "read bytes 0", 0, "bytes"); - MetricAssert.Exists(metrics, "read bytes total", 0, "bytes"); - MetricAssert.Exists(metrics, "read io operations 0", 0, "I/Os"); - MetricAssert.Exists(metrics, "read io operations total", 0, "I/Os"); - MetricAssert.Exists(metrics, "read throughput 0", 0, "MiB/s"); - MetricAssert.Exists(metrics, "read throughput total", 0, "MiB/s"); - MetricAssert.Exists(metrics, "read iops 0", 0, "iops"); - MetricAssert.Exists(metrics, "read iops total", 0, "iops"); - MetricAssert.Exists(metrics, "read latency average 0", 0, "ms"); - MetricAssert.Exists(metrics, "read latency average total", 0, "ms"); - // Write MetricAssert.Exists(metrics, "write bytes 0", 23594541056, "bytes"); MetricAssert.Exists(metrics, "write io operations 0", 2880193, "I/Os"); @@ -213,8 +202,8 @@ public void DiskSpdParserVerifyWriteOnly() [Test] public void DiskSpdParserVerifyForCoreCountGreaterThan64WhichAddsProcessorGrouping() { - string results = File.ReadAllText(MockFixture.GetDirectory(typeof(DiskSpdMetricsParserTests), "Examples", "DiskSpd", "Read8k.txt")); - var parser = new DiskSpdMetricsParser(results); + string results = File.ReadAllText(MockFixture.GetDirectory(typeof(DiskSpdMetricsParserTests), "Examples", "DiskSpd", "Write8k.txt")); + var parser = new DiskSpdMetricsParser(results, "diskspd.exe -b8K -r8K -t32 -o16 -w100 -d900 -Suw -W30 -D -L -Rtext D:\\diskspd-test.dat"); IList metrics = parser.Parse(); @@ -236,11 +225,6 @@ public void DiskSpdParserVerifyForCoreCountGreaterThan64WhichAddsProcessorGroupi MetricAssert.Exists(metrics, "total throughput 1", 36.17, "MiB/s"); MetricAssert.Exists(metrics, "total throughput total", 2579.05, "MiB/s"); - // Read - MetricAssert.Exists(metrics, "read bytes 0", 0, "bytes"); - MetricAssert.Exists(metrics, "read bytes 1", 0, "bytes"); - MetricAssert.Exists(metrics, "read bytes total", 0, "bytes"); - // Write MetricAssert.Exists(metrics, "write bytes 0", 1927421952, "bytes"); MetricAssert.Exists(metrics, "write bytes 1", 2276425728, "bytes"); diff --git a/src/VirtualClient/VirtualClient.Actions.UnitTests/Examples/DiskSpd/Read8k.txt b/src/VirtualClient/VirtualClient.Actions.UnitTests/Examples/DiskSpd/Write8k.txt similarity index 100% rename from src/VirtualClient/VirtualClient.Actions.UnitTests/Examples/DiskSpd/Read8k.txt rename to src/VirtualClient/VirtualClient.Actions.UnitTests/Examples/DiskSpd/Write8k.txt diff --git a/src/VirtualClient/VirtualClient.Actions/DiskSpd/DiskSpdExecutor.cs b/src/VirtualClient/VirtualClient.Actions/DiskSpd/DiskSpdExecutor.cs index 1f26f0f228..bbe5961c09 100644 --- a/src/VirtualClient/VirtualClient.Actions/DiskSpd/DiskSpdExecutor.cs +++ b/src/VirtualClient/VirtualClient.Actions/DiskSpd/DiskSpdExecutor.cs @@ -349,7 +349,7 @@ private void CaptureMetrics(DiskWorkloadProcess workload, EventContext telemetry this.MetadataContract.Apply(telemetryContext); string result = workload.StandardOutput.ToString(); - IList metrics = new DiskSpdMetricsParser(result).Parse(); + IList metrics = new DiskSpdMetricsParser(result, this.CommandLine).Parse(); this.Logger.LogMetrics( "DiskSpd", diff --git a/src/VirtualClient/VirtualClient.Actions/DiskSpd/DiskSpdMetricsParser.cs b/src/VirtualClient/VirtualClient.Actions/DiskSpd/DiskSpdMetricsParser.cs index 44040d7fad..c445159238 100644 --- a/src/VirtualClient/VirtualClient.Actions/DiskSpd/DiskSpdMetricsParser.cs +++ b/src/VirtualClient/VirtualClient.Actions/DiskSpd/DiskSpdMetricsParser.cs @@ -32,39 +32,29 @@ public class DiskSpdMetricsParser : MetricsParser /// private static readonly Regex DashLineRegex = new Regex(@"(-){2,}(\s)*", RegexOptions.ExplicitCapture); + private string commandLine; + private ReadWriteMode readWriteMode; + private List metrics; + /// /// Constructor for /// /// Raw text to parse. - public DiskSpdMetricsParser(string rawText) + /// DiskSpd commandline + public DiskSpdMetricsParser(string rawText, string commandLine) : base(rawText) { + this.commandLine = commandLine; + this.metrics = new List(); + this.readWriteMode = ReadWriteMode.ReadWrite; } - /// - /// Cpu usage. - /// - public DataTable CpuUsage { get; set; } - - /// - /// Total IO result table - /// - public DataTable TotalIo { get; set; } - - /// - /// Read IO result table - /// - public DataTable ReadIo { get; set; } - - /// - /// Write IO result table - /// - public DataTable WriteIo { get; set; } - - /// - /// Latency result table - /// - public DataTable Latency { get; set; } + private enum ReadWriteMode + { + ReadWrite, + ReadOnly, + WriteOnly, + } /// public override IList Parse() @@ -76,38 +66,20 @@ public override IList Parse() this.ParseCPUResult(); this.ParseTotalIoResult(); - this.ParseReadIoResult(); - this.ParseWriteIoResult(); + + if (this.readWriteMode != ReadWriteMode.WriteOnly) + { + this.ParseReadIoResult(); + } + + if (this.readWriteMode != ReadWriteMode.ReadOnly) + { + this.ParseWriteIoResult(); + } + this.ParseLatencyResult(); - List metrics = new List(); - metrics.AddRange(this.CpuUsage.GetMetrics(nameIndex: 0, valueIndex: 1, unit: "percentage", namePrefix: $"cpu {this.CpuUsage.Columns[1].ColumnName.ToLower()} ", metricRelativity: MetricRelativity.LowerIsBetter)); - metrics.AddRange(this.CpuUsage.GetMetrics(nameIndex: 0, valueIndex: 2, unit: "percentage", namePrefix: $"cpu {this.CpuUsage.Columns[2].ColumnName.ToLower()} ", metricRelativity: MetricRelativity.LowerIsBetter)); - metrics.AddRange(this.CpuUsage.GetMetrics(nameIndex: 0, valueIndex: 3, unit: "percentage", namePrefix: $"cpu {this.CpuUsage.Columns[3].ColumnName.ToLower()} ", metricRelativity: MetricRelativity.LowerIsBetter)); - - metrics.AddRange(this.TotalIo.GetMetrics(nameIndex: 0, valueIndex: 1, unit: "bytes", namePrefix: $"total {this.TotalIo.Columns[1].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); - metrics.AddRange(this.TotalIo.GetMetrics(nameIndex: 0, valueIndex: 2, unit: "I/Os", namePrefix: $"total {this.TotalIo.Columns[2].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); - metrics.AddRange(this.TotalIo.GetMetrics(nameIndex: 0, valueIndex: 3, unit: "MiB/s", namePrefix: $"total {this.TotalIo.Columns[3].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); - metrics.AddRange(this.TotalIo.GetMetrics(nameIndex: 0, valueIndex: 4, unit: "iops", namePrefix: $"total {this.TotalIo.Columns[4].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); - metrics.AddRange(this.TotalIo.GetMetrics(nameIndex: 0, valueIndex: 5, unit: "ms", namePrefix: $"total {this.TotalIo.Columns[5].ColumnName} ", metricRelativity: MetricRelativity.LowerIsBetter)); - - metrics.AddRange(this.ReadIo.GetMetrics(nameIndex: 0, valueIndex: 1, unit: "bytes", namePrefix: $"read {this.ReadIo.Columns[1].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); - metrics.AddRange(this.ReadIo.GetMetrics(nameIndex: 0, valueIndex: 2, unit: "I/Os", namePrefix: $"read {this.ReadIo.Columns[2].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); - metrics.AddRange(this.ReadIo.GetMetrics(nameIndex: 0, valueIndex: 3, unit: "MiB/s", namePrefix: $"read {this.ReadIo.Columns[3].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); - metrics.AddRange(this.ReadIo.GetMetrics(nameIndex: 0, valueIndex: 4, unit: "iops", namePrefix: $"read {this.ReadIo.Columns[4].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); - metrics.AddRange(this.ReadIo.GetMetrics(nameIndex: 0, valueIndex: 5, unit: "ms", namePrefix: $"read {this.ReadIo.Columns[5].ColumnName} ", metricRelativity: MetricRelativity.LowerIsBetter)); - - metrics.AddRange(this.WriteIo.GetMetrics(nameIndex: 0, valueIndex: 1, unit: "bytes", namePrefix: $"write {this.WriteIo.Columns[1].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); - metrics.AddRange(this.WriteIo.GetMetrics(nameIndex: 0, valueIndex: 2, unit: "I/Os", namePrefix: $"write {this.WriteIo.Columns[2].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); - metrics.AddRange(this.WriteIo.GetMetrics(nameIndex: 0, valueIndex: 3, unit: "MiB/s", namePrefix: $"write {this.WriteIo.Columns[3].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); - metrics.AddRange(this.WriteIo.GetMetrics(nameIndex: 0, valueIndex: 4, unit: "iops", namePrefix: $"write {this.WriteIo.Columns[4].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); - metrics.AddRange(this.WriteIo.GetMetrics(nameIndex: 0, valueIndex: 5, unit: "ms", namePrefix: $"write {this.WriteIo.Columns[5].ColumnName} ", metricRelativity: MetricRelativity.LowerIsBetter)); - - metrics.AddRange(this.Latency.GetMetrics(nameIndex: 0, valueIndex: 1, unit: "ms", namePrefix: "read latency ", metricRelativity: MetricRelativity.LowerIsBetter)); - metrics.AddRange(this.Latency.GetMetrics(nameIndex: 0, valueIndex: 2, unit: "ms", namePrefix: "write latency ", metricRelativity: MetricRelativity.LowerIsBetter)); - metrics.AddRange(this.Latency.GetMetrics(nameIndex: 0, valueIndex: 3, unit: "ms", namePrefix: "total latency ", metricRelativity: MetricRelativity.LowerIsBetter)); - - return metrics; + return this.metrics; } catch (Exception exc) { @@ -186,6 +158,15 @@ protected override void Preprocess() // MiB/s -> throughput this.PreprocessedText = this.PreprocessedText.Replace("MiB/s", "throughput"); + + if (this.commandLine.Contains("-w100")) + { + this.readWriteMode = ReadWriteMode.WriteOnly; + } + else if (this.commandLine.Contains("-w0")) + { + this.readWriteMode = ReadWriteMode.ReadOnly; + } } private void ParseCPUResult() @@ -197,36 +178,62 @@ private void ParseCPUResult() this.Sections[sectionName] = this.ProcessAndUpdateString(this.Sections[sectionName]); } - this.CpuUsage = DataTableExtensions.ConvertToDataTable( + DataTable cpuUsage = DataTableExtensions.ConvertToDataTable( this.Sections[sectionName], DiskSpdMetricsParser.DiskSpdDataTableDelimiter, sectionName, columnNames: null); + + this.metrics.AddRange(cpuUsage.GetMetrics(nameIndex: 0, valueIndex: 1, unit: "percentage", namePrefix: $"cpu {cpuUsage.Columns[1].ColumnName.ToLower()} ", metricRelativity: MetricRelativity.LowerIsBetter)); + this.metrics.AddRange(cpuUsage.GetMetrics(nameIndex: 0, valueIndex: 2, unit: "percentage", namePrefix: $"cpu {cpuUsage.Columns[2].ColumnName.ToLower()} ", metricRelativity: MetricRelativity.LowerIsBetter)); + this.metrics.AddRange(cpuUsage.GetMetrics(nameIndex: 0, valueIndex: 3, unit: "percentage", namePrefix: $"cpu {cpuUsage.Columns[3].ColumnName.ToLower()} ", metricRelativity: MetricRelativity.LowerIsBetter)); } private void ParseTotalIoResult() { string sectionName = "Total IO"; - this.TotalIo = DataTableExtensions.ConvertToDataTable( + DataTable totalIo = DataTableExtensions.ConvertToDataTable( this.Sections[sectionName], DiskSpdMetricsParser.DiskSpdDataTableDelimiter, sectionName, columnNames: null); + + this.metrics.AddRange(totalIo.GetMetrics(nameIndex: 0, valueIndex: 1, unit: "bytes", namePrefix: $"total {totalIo.Columns[1].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); + this.metrics.AddRange(totalIo.GetMetrics(nameIndex: 0, valueIndex: 2, unit: "I/Os", namePrefix: $"total {totalIo.Columns[2].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); + this.metrics.AddRange(totalIo.GetMetrics(nameIndex: 0, valueIndex: 3, unit: "MiB/s", namePrefix: $"total {totalIo.Columns[3].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); + this.metrics.AddRange(totalIo.GetMetrics(nameIndex: 0, valueIndex: 4, unit: "iops", namePrefix: $"total {totalIo.Columns[4].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); + this.metrics.AddRange(totalIo.GetMetrics(nameIndex: 0, valueIndex: 5, unit: "ms", namePrefix: $"total {totalIo.Columns[5].ColumnName} ", metricRelativity: MetricRelativity.LowerIsBetter)); } private void ParseReadIoResult() { string sectionName = "Read IO"; - this.ReadIo = DataTableExtensions.ConvertToDataTable( + DataTable readIo = DataTableExtensions.ConvertToDataTable( this.Sections[sectionName], DiskSpdMetricsParser.DiskSpdDataTableDelimiter, sectionName, columnNames: null); + + this.metrics.AddRange(readIo.GetMetrics(nameIndex: 0, valueIndex: 1, unit: "bytes", namePrefix: $"read {readIo.Columns[1].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); + this.metrics.AddRange(readIo.GetMetrics(nameIndex: 0, valueIndex: 2, unit: "I/Os", namePrefix: $"read {readIo.Columns[2].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); + this.metrics.AddRange(readIo.GetMetrics(nameIndex: 0, valueIndex: 3, unit: "MiB/s", namePrefix: $"read {readIo.Columns[3].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); + this.metrics.AddRange(readIo.GetMetrics(nameIndex: 0, valueIndex: 4, unit: "iops", namePrefix: $"read {readIo.Columns[4].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); + this.metrics.AddRange(readIo.GetMetrics(nameIndex: 0, valueIndex: 5, unit: "ms", namePrefix: $"read {readIo.Columns[5].ColumnName} ", metricRelativity: MetricRelativity.LowerIsBetter)); } private void ParseWriteIoResult() { string sectionName = "Write IO"; - this.WriteIo = DataTableExtensions.ConvertToDataTable( + DataTable writeIo = DataTableExtensions.ConvertToDataTable( this.Sections[sectionName], DiskSpdMetricsParser.DiskSpdDataTableDelimiter, sectionName, columnNames: null); + + this.metrics.AddRange(writeIo.GetMetrics(nameIndex: 0, valueIndex: 1, unit: "bytes", namePrefix: $"write {writeIo.Columns[1].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); + this.metrics.AddRange(writeIo.GetMetrics(nameIndex: 0, valueIndex: 2, unit: "I/Os", namePrefix: $"write {writeIo.Columns[2].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); + this.metrics.AddRange(writeIo.GetMetrics(nameIndex: 0, valueIndex: 3, unit: "MiB/s", namePrefix: $"write {writeIo.Columns[3].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); + this.metrics.AddRange(writeIo.GetMetrics(nameIndex: 0, valueIndex: 4, unit: "iops", namePrefix: $"write {writeIo.Columns[4].ColumnName} ", metricRelativity: MetricRelativity.HigherIsBetter)); + this.metrics.AddRange(writeIo.GetMetrics(nameIndex: 0, valueIndex: 5, unit: "ms", namePrefix: $"write {writeIo.Columns[5].ColumnName} ", metricRelativity: MetricRelativity.LowerIsBetter)); } private void ParseLatencyResult() { string sectionName = "Latency"; - this.Latency = DataTableExtensions.ConvertToDataTable( + DataTable latency = DataTableExtensions.ConvertToDataTable( this.Sections[sectionName], DiskSpdMetricsParser.DiskSpdDataTableDelimiter, sectionName, columnNames: null); + + this.metrics.AddRange(latency.GetMetrics(nameIndex: 0, valueIndex: 1, unit: "ms", namePrefix: "read latency ", metricRelativity: MetricRelativity.LowerIsBetter)); + this.metrics.AddRange(latency.GetMetrics(nameIndex: 0, valueIndex: 2, unit: "ms", namePrefix: "write latency ", metricRelativity: MetricRelativity.LowerIsBetter)); + this.metrics.AddRange(latency.GetMetrics(nameIndex: 0, valueIndex: 3, unit: "ms", namePrefix: "total latency ", metricRelativity: MetricRelativity.LowerIsBetter)); } private string ProcessAndUpdateString(string input) diff --git a/src/VirtualClient/VirtualClient.TestFramework.UnitTests/DataTableExtensionsUnitTests.cs b/src/VirtualClient/VirtualClient.TestFramework.UnitTests/DataTableExtensionsUnitTests.cs deleted file mode 100644 index c4df25a310..0000000000 --- a/src/VirtualClient/VirtualClient.TestFramework.UnitTests/DataTableExtensionsUnitTests.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace VirtualClient -{ - using System.IO; - using System.Reflection; - using NUnit.Framework; - using VirtualClient.Actions; - - [TestFixture] - [Category("Unit")] - public class DataTableExtensionsUnitTests - { - private string diskSpdResultRawText; - - [SetUp] - public void Setup() - { - string workingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - string gbOutputPath = Path.Combine(workingDirectory, "Examples", "DiskSpdExample.txt"); - this.diskSpdResultRawText = File.ReadAllText(gbOutputPath); - } - - [Test] - public void DataTableExtensionCanSectionizeBasedOnRegex() - { - DiskSpdMetricsParser parser = new DiskSpdMetricsParser(this.diskSpdResultRawText); - parser.Parse(); - } - } -} \ No newline at end of file