Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wasm] Add console version of browser-bench sample #60733

Merged
2 changes: 1 addition & 1 deletion src/mono/sample/wasm/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<Exec Command="$(_Dotnet) publish /p:Configuration=$(Configuration) /p:TargetArchitecture=wasm /p:TargetOS=Browser $(_AOTFlag) $(_SampleProject)" />
</Target>
<Target Name="RunSampleWithV8" DependsOnTargets="BuildSampleInTree">
<Exec Command="cd bin/$(Configuration)/AppBundle &amp;&amp; v8 --expose_wasm runtime.js -- $(DOTNET_MONO_LOG_LEVEL) --run Wasm.Console.Sample.dll" IgnoreExitCode="true" />
<Exec WorkingDirectory="bin/$(Configuration)/AppBundle" Command="v8 --expose_wasm runtime.js -- $(DOTNET_MONO_LOG_LEVEL) --run $(_SampleAssembly) $(Args)" IgnoreExitCode="true" />
</Target>
<Target Name="CheckServe">
<Exec Command="dotnet tool install -g dotnet-serve" IgnoreExitCode="true" />
Expand Down
2 changes: 2 additions & 0 deletions src/mono/sample/wasm/browser-bench/BenchTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ abstract class BenchTask
readonly List<Result> results = new();
public Regex pattern;

public virtual bool BrowserOnly => false;

public async Task<string> RunBatch(List<Result> results, int measurementIdx, int milliseconds = 5000)
{
var measurement = Measurements[measurementIdx];
Expand Down
13 changes: 13 additions & 0 deletions src/mono/sample/wasm/browser-bench/Browser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Sample
{
public partial class Test
{
public static int Main(string[] args)
{
return 0;
}
}
}
72 changes: 72 additions & 0 deletions src/mono/sample/wasm/browser-bench/Console/Console.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using Mono.Options;

namespace Sample
{
public partial class Test
{
static string tasksArg;

static List<string> ProcessArguments(string[] args)
{
var help = false;
var options = new OptionSet {
"Simple mono wasm benchmark",
"",
"Copyright 2021 Microsoft Corporation",
"",
"Options:",
{ "h|help|?",
"Show this message and exit",
v => help = v != null },
{ "t|tasks=",
"Filter comma separated tasks and its measurements matching, TASK[:REGEX][,TASK[:REGEX],...]. Example: -t Json:non,Exceptions:Inline",
v => tasksArg = v },
};

var remaining = options.Parse(args);

if (help || remaining.Count > 0)
{
options.WriteOptionDescriptions(Console.Out);

Environment.Exit(0);
}

return remaining;
}

public static async Task<int> Main(string[] args)
{
ProcessArguments(args);

if (tasksArg != null)
SetTasks(tasksArg);

string output;

instance.formatter = new PlainFormatter();
instance.tasks.RemoveAll(t => t.BrowserOnly);

if (instance.tasks.Count < 1)
{
Console.WriteLine("No task(s) to run");
Environment.Exit(0);
}

do
{
output = await instance.RunTasks();
Console.Write(output);
} while (!string.IsNullOrEmpty(output));

return 0;
}
}
}
12 changes: 12 additions & 0 deletions src/mono/sample/wasm/browser-bench/Console/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
TOP=../../../../../..

include ../../wasm.mk

ifneq ($(AOT),)
override MSBUILD_ARGS+=/p:RunAOTCompilation=true
endif

PROJECT_NAME=Wasm.Console.Bench.Sample.csproj
CONSOLE_DLL=Wasm.Console.Bench.Sample.dll

run: run-console
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<WasmCopyAppZipToHelixTestDir Condition="'$(ArchiveTests)' == 'true'">true</WasmCopyAppZipToHelixTestDir>
<WasmMainJSPath>$(MonoProjectRoot)\wasm\runtime-test.js</WasmMainJSPath>
<WasmGenerateRunV8Script>true</WasmGenerateRunV8Script>
<SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
</PropertyGroup>

<PropertyGroup>
<_SampleProject>Wasm.Console.Bench.Sample.csproj</_SampleProject>
<_SampleAssembly>Wasm.Console.Bench.Sample.dll</_SampleAssembly>
<SignAssembly>False</SignAssembly>
</PropertyGroup>

<Target Name="RunSample" DependsOnTargets="RunSampleWithV8" />

<ItemGroup>
<Compile Include="../*.cs" />
<Compile Remove="../Browser.cs" />
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
</ItemGroup>
</Project>
86 changes: 55 additions & 31 deletions src/mono/sample/wasm/browser-bench/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,21 @@

namespace Sample
{
public class Test
public partial class Test
{
public static void Main(string[] args)
{
}

BenchTask[] tasks =
List<BenchTask> tasks = new()
{
new ExceptionsTask(),
new JsonTask (),
new WebSocketTask()
};
static Test instance = new Test ();
static Test instance = new Test();
Formatter formatter = new HTMLFormatter();

[MethodImpl(MethodImplOptions.NoInlining)]
public static Task<string> RunBenchmark()
{
return instance.RunTasks ();
return instance.RunTasks();
}

[MethodImpl(MethodImplOptions.NoInlining)]
Expand All @@ -46,14 +43,15 @@ public static void SetTasks(string taskNames)
var idx = names[i].IndexOf(':');
string name;

if (idx == -1) {
if (idx == -1)
{
name = names[i];
pattern = null;
}
else
{
name = names[i].Substring(0, idx);
pattern = new Regex (names[i][(idx + 1)..]);
pattern = new Regex(names[i][(idx + 1)..]);
}

var taskType = Type.GetType($"Sample.{name}Task");
Expand All @@ -62,10 +60,10 @@ public static void SetTasks(string taskNames)

var task = (BenchTask)Activator.CreateInstance(taskType);
task.pattern = pattern;
tasksList.Add (task);
tasksList.Add(task);
}

instance.tasks = tasksList.ToArray ();
instance.tasks = tasksList;
}

int taskCounter = 0;
Expand All @@ -80,11 +78,12 @@ BenchTask Task
List<BenchTask.Result> results = new();
bool resultsReturned;

bool NextTask ()
bool NextTask()
{
bool hasMeasurement;
do {
if (taskCounter == tasks.Length)
do
{
if (taskCounter == tasks.Count)
return false;

Task = tasks[taskCounter];
Expand All @@ -100,7 +99,7 @@ bool NextTask ()
return true;
}

bool NextMeasurement ()
bool NextMeasurement()
{
runIdx = 0;

Expand All @@ -122,45 +121,46 @@ public async Task<string> RunTasks()
if (resultsReturned)
return "";

if (taskCounter == 0) {
NextTask ();
return "Benchmark started<br>";
if (taskCounter == 0)
{
NextTask();
return $"Benchmark started{formatter.NewLine}";
}

if (measurementIdx == -1)
return ResultsSummary();

if (runIdx >= Task.Measurements [measurementIdx].NumberOfRuns && !NextMeasurement() && !NextTask ())
return ResultsSummary();
if (runIdx >= Task.Measurements[measurementIdx].NumberOfRuns && !NextMeasurement() && !NextTask())
return ResultsSummary();

runIdx++;

return await Task.RunBatch(results, measurementIdx);
return $"{await Task.RunBatch(results, measurementIdx)}{formatter.NewLine}";
}

string ResultsSummary ()
string ResultsSummary()
{
Dictionary<string, double> minTimes = new Dictionary<string, double> ();
Dictionary<string, double> minTimes = new Dictionary<string, double>();
StringBuilder sb = new();

foreach (var result in results)
{
double t;
var key = $"{result.taskName}, {result.measurementName}";
t = result.span.TotalMilliseconds/result.steps;
t = result.span.TotalMilliseconds / result.steps;
if (minTimes.ContainsKey(key))
t = Math.Min (minTimes[key], t);
t = Math.Min(minTimes[key], t);

minTimes[key] = t;
}

sb.Append("<h4>Summary</h4>");
sb.Append($"{formatter.NewLine}Summary{formatter.NewLine}");
foreach (var key in minTimes.Keys)
{
sb.Append($"{key}: {minTimes [key]}ms<br>");
sb.Append($"{key}: {minTimes[key]}ms{formatter.NewLine}");
}

sb.Append("<h4>.md</h4><tt>| measurement | time |<br>|-:|-:|<br>");
sb.Append($"{formatter.NewLine}.md{formatter.NewLine}{formatter.CodeStart}| measurement | time |{formatter.NewLine}|-:|-:|{formatter.NewLine}");
foreach (var key in minTimes.Keys)
{
var time = minTimes[key];
Expand All @@ -170,13 +170,37 @@ string ResultsSummary ()
time *= 1000;
unit = "us";
}
sb.Append($"| {key.Replace('_',' '),38} | {time,10:F4}{unit} |<br>".Replace (" ", "&nbsp;"));
sb.Append($"| {key.Replace('_', ' '),38} | {time,10:F4}{unit} |{formatter.NewLine}".Replace(" ", formatter.NonBreakingSpace));
}
sb.Append("</tt>");
sb.Append($"{formatter.CodeEnd}");

resultsReturned = true;

return sb.ToString();
}
}

public abstract class Formatter
{
public abstract string NewLine { get; }
public abstract string NonBreakingSpace { get; }
public abstract string CodeStart { get; }
public abstract string CodeEnd { get; }
}

public class PlainFormatter : Formatter
{
override public string NewLine => "\n";
override public string NonBreakingSpace => " ";
override public string CodeStart => "";
override public string CodeEnd => "";
}

public class HTMLFormatter : Formatter
{
override public string NewLine => "<br/>";
override public string NonBreakingSpace => "&nbsp;";
override public string CodeStart => "<code>";
override public string CodeEnd => "</code>";
}
}
Loading