Skip to content

Commit 145af7f

Browse files
Add console runner for perf scenarios.
This is to make it easy to execute them under the Mono desktop interpreter to get more accurate profiling data.
1 parent 8b79720 commit 145af7f

File tree

12 files changed

+575
-683
lines changed

12 files changed

+575
-683
lines changed

AspNetCore.sln

Lines changed: 355 additions & 640 deletions
Large diffs are not rendered by default.

src/Components/ComponentsNoDeps.slnf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"src\\Components\\WebAssembly\\Sdk\\integrationtests\\Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj",
3838
"src\\Components\\Web\\src\\Microsoft.AspNetCore.Components.Web.csproj",
3939
"src\\Components\\Web\\test\\Microsoft.AspNetCore.Components.Web.Tests.csproj",
40+
"src\\Components\\benchmarkapps\\Wasm.Performance\\ConsoleHost\\Wasm.Performance.ConsoleHost.csproj",
4041
"src\\Components\\benchmarkapps\\Wasm.Performance\\Driver\\Wasm.Performance.Driver.csproj",
4142
"src\\Components\\benchmarkapps\\Wasm.Performance\\TestApp\\Wasm.Performance.TestApp.csproj",
4243
"src\\Components\\test\\E2ETest\\Microsoft.AspNetCore.Components.E2ETests.csproj",
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Runtime.ExceptionServices;
6+
using System.Threading.Tasks;
7+
using Microsoft.AspNetCore.Components;
8+
using Microsoft.AspNetCore.Components.RenderTree;
9+
using Microsoft.Extensions.Logging;
10+
11+
namespace Wasm.Performance.ConsoleHost
12+
{
13+
class ConsoleHostRenderer : Renderer
14+
{
15+
public ConsoleHostRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
16+
: base(serviceProvider, loggerFactory)
17+
{
18+
}
19+
20+
public override Dispatcher Dispatcher { get; } = new NullDispatcher();
21+
22+
protected override void HandleException(Exception exception)
23+
{
24+
ExceptionDispatchInfo.Capture(exception).Throw();
25+
}
26+
27+
protected override Task UpdateDisplayAsync(in RenderBatch renderBatch)
28+
{
29+
// ConsoleHost is only for profiling the .NET side of execution.
30+
// There isn't a real display to update.
31+
return Task.CompletedTask;
32+
}
33+
34+
// Expose some protected APIs publicly
35+
public new int AssignRootComponentId(IComponent component)
36+
=> base.AssignRootComponentId(component);
37+
38+
public new Task RenderRootComponentAsync(int componentId)
39+
=> base.RenderRootComponentAsync(componentId);
40+
}
41+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Threading.Tasks;
6+
using Microsoft.AspNetCore.Components;
7+
8+
namespace Wasm.Performance.ConsoleHost
9+
{
10+
internal class NullDispatcher : Dispatcher
11+
{
12+
public override bool CheckAccess()
13+
=> true;
14+
15+
public override Task InvokeAsync(Action workItem)
16+
{
17+
workItem();
18+
return Task.CompletedTask;
19+
}
20+
21+
public override Task InvokeAsync(Func<Task> workItem)
22+
=> workItem();
23+
24+
public override Task<TResult> InvokeAsync<TResult>(Func<TResult> workItem)
25+
=> Task.FromResult(workItem());
26+
27+
public override Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> workItem)
28+
=> workItem();
29+
}
30+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.Extensions.CommandLineUtils;
5+
using Wasm.Performance.ConsoleHost.Scenarios;
6+
7+
namespace Wasm.Performance.ConsoleHost
8+
{
9+
class Program : CommandLineApplication
10+
{
11+
static void Main(string[] args)
12+
{
13+
new Program().Execute(args);
14+
}
15+
16+
public Program()
17+
{
18+
OnExecute(() =>
19+
{
20+
ShowHelp();
21+
return 1;
22+
});
23+
24+
Commands.Add(new GridScenario());
25+
}
26+
}
27+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Threading.Tasks;
6+
using Microsoft.Extensions.CommandLineUtils;
7+
using Microsoft.Extensions.DependencyInjection;
8+
using Microsoft.Extensions.Logging;
9+
10+
namespace Wasm.Performance.ConsoleHost.Scenarios
11+
{
12+
abstract class ComponentRenderingScenarioBase : CommandLineApplication
13+
{
14+
protected ComponentRenderingScenarioBase(string name)
15+
{
16+
Name = name;
17+
18+
var cyclesOption = new CommandOption("--cycles", CommandOptionType.SingleValue);
19+
Options.Add(cyclesOption);
20+
21+
OnExecute(() =>
22+
{
23+
var numCycles = cyclesOption.HasValue() ? int.Parse(cyclesOption.Value()) : 1;
24+
25+
var serviceCollection = new ServiceCollection();
26+
PopulateServiceCollection(serviceCollection);
27+
var serviceProvider = serviceCollection.BuildServiceProvider();
28+
29+
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
30+
var renderer = new ConsoleHostRenderer(serviceProvider, loggerFactory);
31+
32+
var startTime = DateTime.Now;
33+
ExecuteAsync(renderer, numCycles).Wait();
34+
35+
var duration = DateTime.Now - startTime;
36+
var durationPerCycle = (duration / numCycles).TotalMilliseconds;
37+
Console.WriteLine($"{Name}: {durationPerCycle:F1}ms per cycle (cycles tested: {numCycles})");
38+
39+
return 0;
40+
});
41+
}
42+
43+
protected virtual void PopulateServiceCollection(IServiceCollection serviceCollection)
44+
{
45+
serviceCollection.AddLogging();
46+
}
47+
48+
protected abstract Task ExecuteAsync(ConsoleHostRenderer renderer, int numCycles);
49+
}
50+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Threading.Tasks;
6+
using Microsoft.Extensions.CommandLineUtils;
7+
using Wasm.Performance.TestApp.Pages;
8+
9+
namespace Wasm.Performance.ConsoleHost.Scenarios
10+
{
11+
class GridScenario : ComponentRenderingScenarioBase
12+
{
13+
readonly CommandOption _gridTypeOption = new CommandOption("--gridtype", CommandOptionType.SingleValue);
14+
15+
public GridScenario() : base("grid")
16+
{
17+
Options.Add(_gridTypeOption);
18+
}
19+
20+
protected override async Task ExecuteAsync(ConsoleHostRenderer renderer, int numCycles)
21+
{
22+
var gridType = _gridTypeOption.HasValue()
23+
? (GridRendering.RenderMode)Enum.Parse(typeof(GridRendering.RenderMode), _gridTypeOption.Value(), true)
24+
: GridRendering.RenderMode.FastGrid;
25+
26+
for (var i = 0; i < numCycles; i++)
27+
{
28+
var hostPage = new GridRendering { SelectedRenderMode = gridType };
29+
hostPage.Show();
30+
31+
var componentId = renderer.AssignRootComponentId(hostPage);
32+
await renderer.RenderRootComponentAsync(componentId);
33+
34+
hostPage.ChangePage();
35+
await renderer.RenderRootComponentAsync(componentId);
36+
}
37+
}
38+
}
39+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
6+
<IsPackable>false</IsPackable>
7+
<IsShipping>false</IsShipping>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<ProjectReference Include="..\TestApp\Wasm.Performance.TestApp.csproj" />
12+
<Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" />
13+
</ItemGroup>
14+
15+
</Project>

src/Components/benchmarkapps/Wasm.Performance/TestApp/BenchmarkEvent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public static class BenchmarkEvent
99
{
1010
public static void Send(IJSRuntime jsRuntime, string name)
1111
{
12-
((IJSInProcessRuntime)jsRuntime).Invoke<object>(
12+
((IJSInProcessRuntime)jsRuntime)?.Invoke<object>(
1313
"receiveBenchmarkEvent",
1414
name);
1515
}

src/Components/benchmarkapps/Wasm.Performance/TestApp/Pages/GridRendering.razor

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
@page "/gridrendering"
1+
@page "/gridrendering"
22
@inject IJSRuntime JSRuntime
33
@using Wasm.Performance.TestApp.Shared.FastGrid
44

55
<h1>20 x 200 Grid</h1>
66

77
<fieldset>
8-
<select id="render-mode" @bind="selectedRenderMode">
8+
<select id="render-mode" @bind="SelectedRenderMode">
99
<option>@RenderMode.FastGrid</option>
1010
<option>@RenderMode.PlainTable</option>
1111
<option>@RenderMode.ComplexTable</option>
@@ -23,7 +23,7 @@
2323
{
2424
<p><em>(No data assigned)</em></p>
2525
}
26-
else if (selectedRenderMode == RenderMode.FastGrid)
26+
else if (SelectedRenderMode == RenderMode.FastGrid)
2727
{
2828
<p>FastGrid represents a minimal, optimized implementation of a grid.</p>
2929

@@ -50,23 +50,23 @@ else if (selectedRenderMode == RenderMode.FastGrid)
5050
<GridColumn TRowData="WeatherForecast" Title="Summary">@context.Summary</GridColumn>
5151
</Grid>
5252
}
53-
else if (selectedRenderMode == RenderMode.PlainTable)
53+
else if (SelectedRenderMode == RenderMode.PlainTable)
5454
{
5555
<p>PlainTable represents a minimal but not optimized implementation of a grid.</p>
5656

5757
<Wasm.Performance.TestApp.Shared.PlainTable.TableComponent Data="@forecasts" Columns="@Columns" />
5858
}
59-
else if (selectedRenderMode == RenderMode.ComplexTable)
59+
else if (SelectedRenderMode == RenderMode.ComplexTable)
6060
{
6161
<p>ComplexTable represents a maximal, not optimized implementation of a grid, using a wide range of Blazor features at once.</p>
6262

6363
<Wasm.Performance.TestApp.Shared.ComplexTable.TableComponent Data="@forecasts" Columns="@Columns" />
6464
}
6565

6666
@code {
67-
enum RenderMode { PlainTable, ComplexTable, FastGrid }
67+
public enum RenderMode { PlainTable, ComplexTable, FastGrid }
6868

69-
private RenderMode selectedRenderMode = RenderMode.FastGrid;
69+
public RenderMode SelectedRenderMode { get; set; } = RenderMode.FastGrid;
7070

7171
private WeatherForecast[] forecasts;
7272
public List<string> Columns { get; set; } = new List<string>
@@ -89,17 +89,17 @@ else if (selectedRenderMode == RenderMode.ComplexTable)
8989
TemperatureC = index,
9090
};
9191

92-
void Show()
92+
public void Show()
9393
{
9494
forecasts = staticSampleDataPage1;
9595
}
9696

97-
void Hide()
97+
public void Hide()
9898
{
9999
forecasts = null;
100100
}
101101

102-
void ChangePage()
102+
public void ChangePage()
103103
{
104104
forecasts = (forecasts == staticSampleDataPage1) ? staticSampleDataPage2 : staticSampleDataPage1;
105105
}

0 commit comments

Comments
 (0)