tools/perf-automation/Azure.Sdk.Tools.PerfAutomation/Rust.cs (140 lines of code) (raw):
using Azure.Sdk.Tools.PerfAutomation.Models;
using Microsoft.Crank.Agent;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using JsonSerializer = System.Text.Json.JsonSerializer;
namespace Azure.Sdk.Tools.PerfAutomation
{
namespace Models.Rust
{
//{ "sampling_mode":"Linear","iters":[216428.0, 432856.0, 649284.0, 865712.0, 1082140.0, 1298568.0, 1514996.0, 1731424.0, 1947852.0, 2164280.0],"times":[89284700.0, 182949300.0, 279225800.0, 370553800.0, 459758900.0, 544908000.0, 647300900.0, 757810300.0, 831844100.0, 926051100.0]}
struct Samples
{
public string SamplingMode { get; set; }
public double[] Iters { get; set; }
public double[] Times { get; set; }
}
}
public class Rust : LanguageBase
{
public class UtilEventArgs : EventArgs
{
public UtilEventArgs(string methodName, string[] methodParams)
{
this.MethodName = methodName;
this.Params = methodParams;
}
public string MethodName { get; set; }
public string[] Params { get; set; } = null;
}
private const string _sdkDirectory = "sdk";
private const string _cargoName = "cargo";
private string _resultsDirectory = Path.Combine("target", "criterion");
private string _targetResultsDirectory;
public bool IsTest { get; set; } = false;
public bool IsWindows { get; set; } = Util.IsWindows;
protected override Language Language => Language.Rust;
public event EventHandler<UtilEventArgs> UtilMethodCall;
public override async Task<(string output, string error, object context)> SetupAsync(
string project,
string languageVersion,
string primaryPackage,
IDictionary<string, string> packageVersions,
bool debug)
{
// just make sure we have the target directory clened up of previous results in case of a test issue in a previous test / run
_targetResultsDirectory = await CleanupAsync(project);
return (String.Empty, String.Empty, String.Empty);
}
public override async Task<IterationResult> RunAsync(
string project,
string languageVersion,
string primaryPackage,
IDictionary<string, string> packageVersions,
string testName,
string arguments,
bool profile,
string profilerOptions,
object context)
{
// set up the params for cargo
string finalParams = $"bench -- \"{testName}\"";
ProcessResult result = new ProcessResult(0, String.Empty, String.Empty);
if (IsTest)
{
UtilMethodCall(this, new UtilEventArgs(
"RunAsync",
new string[] {
_cargoName,
finalParams,
WorkingDirectory}));
result = new ProcessResult(0, "cargo bench result", "error");
}
else
{
result = await Util.RunAsync(_cargoName, finalParams, WorkingDirectory);
}
//parse the samples file for the test and calculate the ops per second
var opsPerSecond = ExtractOpsPerSecond(testName);
return new IterationResult
{
OperationsPerSecond = opsPerSecond,
StandardOutput = result.StandardOutput,
StandardError = result.StandardError,
PackageVersions = packageVersions
};
}
public override async Task<string> CleanupAsync(string project)
{
// we need to find the folder that contains SDK directory. SDK is a sibling of target
var currentDirectory = WorkingDirectory;
bool sdkFolderFound = false;
while (!sdkFolderFound)
{
DirectoryInfo directory = new DirectoryInfo(Path.Combine(currentDirectory, _sdkDirectory));
if (directory.Exists)
{
sdkFolderFound = true;
}
else
{
currentDirectory = (new DirectoryInfo(currentDirectory)).Parent.FullName;
}
}
// now that we have the path to the target directory wwe cand determine the results directory .../target/criterion
var resultsDirectory = Path.Combine(currentDirectory, _resultsDirectory);
if (IsTest)
{
UtilMethodCall(this, new UtilEventArgs("DeleteIfExists", new string[] { resultsDirectory }));
}
else
{
Util.DeleteIfExists(resultsDirectory);
}
await Task.Delay(0);
// return the results directory so we can use it later on and avoid the discovery
return resultsDirectory;
}
private double ExtractOpsPerSecond(string testName)
{
// the results are in the target directory under target/criterion/<testName>/new
var results = ParseFromJsonFile(Path.Combine(_targetResultsDirectory, testName, "new", "sample.json"));
double nanos = 0.0;
// the timings are in nanos, also we have to divide the time by the number of iterations to get the time for one operation
for (int i = 0; i < results.Iters.Length; i++)
{
nanos += results.Times[i] / results.Iters[i];
}
nanos /= results.Iters.Length;
// once we have the timing of one operation( in nano seconds) divide one sec in nanos by the time to get ops/sec
var opsPerSecond = Math.Pow(10, 9) / nanos;
return opsPerSecond;
}
private Models.Rust.Samples ParseFromJsonFile(string filePath)
{
// open the file
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"The file '{filePath}' does not exist.");
}
var jsonContent = File.ReadAllText(filePath);
// deserialize in a Samples struct
return JsonSerializer.Deserialize<Models.Rust.Samples>(jsonContent, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true // Allows case-insensitive matching of JSON property names
});
}
}
}