Add console runner for measuring Blazor perf on desktop interpreter (#24469)

This commit is contained in:
Steve Sanderson 2020-08-03 21:42:44 +01:00 committed by GitHub
parent 0335ac60a6
commit 1885af9634
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 237 additions and 43 deletions

View File

@ -1455,6 +1455,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InteropClient", "src\Grpc\t
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InteropWebsite", "src\Grpc\test\testassets\InteropWebsite\InteropWebsite.csproj", "{19189670-E206-471D-94F8-7D3D545E5020}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wasm.Performance.ConsoleHost", "src\Components\benchmarkapps\Wasm.Performance\ConsoleHost\Wasm.Performance.ConsoleHost.csproj", "{E9408723-E6A9-4715-B906-3B25B0238ABA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -6889,6 +6891,18 @@ Global
{19189670-E206-471D-94F8-7D3D545E5020}.Release|x64.Build.0 = Release|Any CPU
{19189670-E206-471D-94F8-7D3D545E5020}.Release|x86.ActiveCfg = Release|Any CPU
{19189670-E206-471D-94F8-7D3D545E5020}.Release|x86.Build.0 = Release|Any CPU
{E9408723-E6A9-4715-B906-3B25B0238ABA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E9408723-E6A9-4715-B906-3B25B0238ABA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9408723-E6A9-4715-B906-3B25B0238ABA}.Debug|x64.ActiveCfg = Debug|Any CPU
{E9408723-E6A9-4715-B906-3B25B0238ABA}.Debug|x64.Build.0 = Debug|Any CPU
{E9408723-E6A9-4715-B906-3B25B0238ABA}.Debug|x86.ActiveCfg = Debug|Any CPU
{E9408723-E6A9-4715-B906-3B25B0238ABA}.Debug|x86.Build.0 = Debug|Any CPU
{E9408723-E6A9-4715-B906-3B25B0238ABA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9408723-E6A9-4715-B906-3B25B0238ABA}.Release|Any CPU.Build.0 = Release|Any CPU
{E9408723-E6A9-4715-B906-3B25B0238ABA}.Release|x64.ActiveCfg = Release|Any CPU
{E9408723-E6A9-4715-B906-3B25B0238ABA}.Release|x64.Build.0 = Release|Any CPU
{E9408723-E6A9-4715-B906-3B25B0238ABA}.Release|x86.ActiveCfg = Release|Any CPU
{E9408723-E6A9-4715-B906-3B25B0238ABA}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -7618,6 +7632,7 @@ Global
{00B2DD87-7E2A-4460-BE1B-5E18B1062B7F} = {E763DA15-8F4E-446C-99B8-309053C75598}
{C3A0F425-669F-46A8-893F-CF449A6DAE56} = {00B2DD87-7E2A-4460-BE1B-5E18B1062B7F}
{19189670-E206-471D-94F8-7D3D545E5020} = {00B2DD87-7E2A-4460-BE1B-5E18B1062B7F}
{E9408723-E6A9-4715-B906-3B25B0238ABA} = {6276A9A0-791B-49C1-AD8F-50AC47CDC196}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}

View File

@ -37,6 +37,7 @@
"src\\Components\\WebAssembly\\Sdk\\integrationtests\\Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj",
"src\\Components\\Web\\src\\Microsoft.AspNetCore.Components.Web.csproj",
"src\\Components\\Web\\test\\Microsoft.AspNetCore.Components.Web.Tests.csproj",
"src\\Components\\benchmarkapps\\Wasm.Performance\\ConsoleHost\\Wasm.Performance.ConsoleHost.csproj",
"src\\Components\\benchmarkapps\\Wasm.Performance\\Driver\\Wasm.Performance.Driver.csproj",
"src\\Components\\benchmarkapps\\Wasm.Performance\\TestApp\\Wasm.Performance.TestApp.csproj",
"src\\Components\\test\\E2ETest\\Microsoft.AspNetCore.Components.E2ETests.csproj",

View File

@ -0,0 +1,41 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.Extensions.Logging;
namespace Wasm.Performance.ConsoleHost
{
internal class ConsoleHostRenderer : Renderer
{
public ConsoleHostRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
: base(serviceProvider, loggerFactory)
{
}
public override Dispatcher Dispatcher { get; } = new NullDispatcher();
protected override void HandleException(Exception exception)
{
ExceptionDispatchInfo.Capture(exception).Throw();
}
protected override Task UpdateDisplayAsync(in RenderBatch renderBatch)
{
// ConsoleHost is only for profiling the .NET side of execution.
// There isn't a real display to update.
return Task.CompletedTask;
}
// Expose some protected APIs publicly
public new int AssignRootComponentId(IComponent component)
=> base.AssignRootComponentId(component);
public new Task RenderRootComponentAsync(int componentId)
=> base.RenderRootComponentAsync(componentId);
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
namespace Wasm.Performance.ConsoleHost
{
internal class NullDispatcher : Dispatcher
{
public override bool CheckAccess()
=> true;
public override Task InvokeAsync(Action workItem)
{
workItem();
return Task.CompletedTask;
}
public override Task InvokeAsync(Func<Task> workItem)
=> workItem();
public override Task<TResult> InvokeAsync<TResult>(Func<TResult> workItem)
=> Task.FromResult(workItem());
public override Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> workItem)
=> workItem();
}
}

View File

@ -0,0 +1,27 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Extensions.CommandLineUtils;
using Wasm.Performance.ConsoleHost.Scenarios;
namespace Wasm.Performance.ConsoleHost
{
internal class Program : CommandLineApplication
{
static void Main(string[] args)
{
new Program().Execute(args);
}
public Program()
{
OnExecute(() =>
{
ShowHelp();
return 1;
});
Commands.Add(new GridScenario());
}
}
}

View File

@ -0,0 +1,50 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.CommandLineUtils;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Wasm.Performance.ConsoleHost.Scenarios
{
internal abstract class ComponentRenderingScenarioBase : CommandLineApplication
{
protected ComponentRenderingScenarioBase(string name)
{
Name = name;
var cyclesOption = new CommandOption("--cycles", CommandOptionType.SingleValue);
Options.Add(cyclesOption);
OnExecute(() =>
{
var numCycles = cyclesOption.HasValue() ? int.Parse(cyclesOption.Value()) : 1;
var serviceCollection = new ServiceCollection();
PopulateServiceCollection(serviceCollection);
var serviceProvider = serviceCollection.BuildServiceProvider();
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
var renderer = new ConsoleHostRenderer(serviceProvider, loggerFactory);
var startTime = DateTime.Now;
ExecuteAsync(renderer, numCycles).Wait();
var duration = DateTime.Now - startTime;
var durationPerCycle = (duration / numCycles).TotalMilliseconds;
Console.WriteLine($"{Name}: {durationPerCycle:F1}ms per cycle (cycles tested: {numCycles})");
return 0;
});
}
protected virtual void PopulateServiceCollection(IServiceCollection serviceCollection)
{
serviceCollection.AddLogging();
}
protected abstract Task ExecuteAsync(ConsoleHostRenderer renderer, int numCycles);
}
}

View File

@ -0,0 +1,39 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.CommandLineUtils;
using Wasm.Performance.TestApp.Pages;
namespace Wasm.Performance.ConsoleHost.Scenarios
{
internal class GridScenario : ComponentRenderingScenarioBase
{
readonly CommandOption _gridTypeOption = new CommandOption("--gridtype", CommandOptionType.SingleValue);
public GridScenario() : base("grid")
{
Options.Add(_gridTypeOption);
}
protected override async Task ExecuteAsync(ConsoleHostRenderer renderer, int numCycles)
{
var gridType = _gridTypeOption.HasValue()
? (GridRendering.RenderMode)Enum.Parse(typeof(GridRendering.RenderMode), _gridTypeOption.Value(), true)
: GridRendering.RenderMode.FastGrid;
for (var i = 0; i < numCycles; i++)
{
var hostPage = new GridRendering { SelectedRenderMode = gridType };
hostPage.Show();
var componentId = renderer.AssignRootComponentId(hostPage);
await renderer.RenderRootComponentAsync(componentId);
hostPage.ChangePage();
await renderer.RenderRootComponentAsync(componentId);
}
}
}
}

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<IsPackable>false</IsPackable>
<IsShipping>false</IsShipping>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\TestApp\Wasm.Performance.TestApp.csproj" />
<Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" />
</ItemGroup>
</Project>

View File

@ -9,7 +9,9 @@ namespace Wasm.Performance.TestApp
{
public static void Send(IJSRuntime jsRuntime, string name)
{
((IJSInProcessRuntime)jsRuntime).Invoke<object>(
// jsRuntime will be null if we're in an environment without any
// JS runtime, e.g., the console runner
((IJSInProcessRuntime)jsRuntime)?.Invoke<object>(
"receiveBenchmarkEvent",
name);
}

View File

@ -1,11 +1,11 @@
@page "/gridrendering"
@page "/gridrendering"
@inject IJSRuntime JSRuntime
@using Wasm.Performance.TestApp.Shared.FastGrid
<h1>20 x 200 Grid</h1>
<fieldset>
<select id="render-mode" @bind="selectedRenderMode">
<select id="render-mode" @bind="SelectedRenderMode">
<option>@RenderMode.FastGrid</option>
<option>@RenderMode.PlainTable</option>
<option>@RenderMode.ComplexTable</option>
@ -23,7 +23,7 @@
{
<p><em>(No data assigned)</em></p>
}
else if (selectedRenderMode == RenderMode.FastGrid)
else if (SelectedRenderMode == RenderMode.FastGrid)
{
<p>FastGrid represents a minimal, optimized implementation of a grid.</p>
@ -50,13 +50,13 @@ else if (selectedRenderMode == RenderMode.FastGrid)
<GridColumn TRowData="WeatherForecast" Title="Summary">@context.Summary</GridColumn>
</Grid>
}
else if (selectedRenderMode == RenderMode.PlainTable)
else if (SelectedRenderMode == RenderMode.PlainTable)
{
<p>PlainTable represents a minimal but not optimized implementation of a grid.</p>
<Wasm.Performance.TestApp.Shared.PlainTable.TableComponent Data="@forecasts" Columns="@Columns" />
}
else if (selectedRenderMode == RenderMode.ComplexTable)
else if (SelectedRenderMode == RenderMode.ComplexTable)
{
<p>ComplexTable represents a maximal, not optimized implementation of a grid, using a wide range of Blazor features at once.</p>
@ -64,9 +64,9 @@ else if (selectedRenderMode == RenderMode.ComplexTable)
}
@code {
enum RenderMode { PlainTable, ComplexTable, FastGrid }
public enum RenderMode { PlainTable, ComplexTable, FastGrid }
private RenderMode selectedRenderMode = RenderMode.FastGrid;
public RenderMode SelectedRenderMode { get; set; } = RenderMode.FastGrid;
private WeatherForecast[] forecasts;
public List<string> Columns { get; set; } = new List<string>
@ -89,17 +89,17 @@ else if (selectedRenderMode == RenderMode.ComplexTable)
TemperatureC = index,
};
void Show()
public void Show()
{
forecasts = staticSampleDataPage1;
}
void Hide()
public void Hide()
{
forecasts = null;
}
void ChangePage()
public void ChangePage()
{
forecasts = (forecasts == staticSampleDataPage1) ? staticSampleDataPage2 : staticSampleDataPage1;
}

View File

@ -1,4 +1,4 @@
@using WeatherForecast = Pages.GridRendering.WeatherForecast
@using WeatherForecast = Pages.GridRendering.WeatherForecast
<table class="table">
<thead>
@ -13,7 +13,7 @@
<CascadingValue Value="@this">
<RowCollection Data="@Data"
Columns="@Columns"
OnClick="@RefreshComponent"></RowCollection>
OnClick="@HandleClickEvent"></RowCollection>
</CascadingValue>
</tbody>
</table>
@ -26,21 +26,8 @@
[Parameter]
public List<string> Columns { get; set; }
DateTime t1;
DateTime t2;
Task RefreshComponent(int index)
Task HandleClickEvent(int index)
{
t1 = DateTime.Now;
StateHasChanged();
return Task.CompletedTask;
}
protected override Task OnAfterRenderAsync(bool firstRender)
{
if (!firstRender)
{
t2 = DateTime.Now;
Console.WriteLine("Refresh Time " + (t2 - t1).TotalMilliseconds);
}
return base.OnAfterRenderAsync(firstRender);
}
}

View File

@ -1,4 +1,4 @@
@using WeatherForecast = Pages.GridRendering.WeatherForecast
@using WeatherForecast = Pages.GridRendering.WeatherForecast
<table class="table">
<thead>
@ -12,7 +12,7 @@
<tbody>
<RowCollection Data="@Data"
Columns="@Columns"
OnClick="@RefreshComponent"></RowCollection>
OnClick="@HandleClickEvent"></RowCollection>
</tbody>
</table>
@ -24,21 +24,8 @@
[Parameter]
public List<string> Columns { get; set; }
DateTime t1;
DateTime t2;
Task RefreshComponent(int index)
Task HandleClickEvent(int index)
{
t1 = DateTime.Now;
StateHasChanged();
return Task.CompletedTask;
}
protected override Task OnAfterRenderAsync(bool firstRender)
{
if (!firstRender)
{
t2 = DateTime.Now;
Console.WriteLine("Refresh Time " + (t2 - t1).TotalMilliseconds);
}
return base.OnAfterRenderAsync(firstRender);
}
}