Skip to content

Commit 31f63d9

Browse files
committed
Add a perf scenario involing nested components + editing
* Adds additional scenarios to address #17011 * Include commit hash so we can track the build of Blazor WASM associated with a perf run * Port some infrastructure fixes from master Fixes #17011
1 parent c935e9a commit 31f63d9

File tree

14 files changed

+251
-37
lines changed

14 files changed

+251
-37
lines changed

eng/Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343

4444
<!-- Exclude the benchmarks because they use <PackageReference>. -->
4545
<ProjectToExclude Include="
46-
$(RepoRoot)src\Components\benchmarkapps\**\*.csproj;
46+
$(RepoRoot)src\Components\benchmarkapps\BlazingPizza.Server\**\*.csproj;
4747
$(RepoRoot)src\Mvc\benchmarkapps\**\*.csproj;
4848
$(RepoRoot)src\Servers\Kestrel\perf\PlatformBenchmarks\**\*.csproj;
4949
$(RepoRoot)src\SignalR\perf\benchmarkapps\**\*.csproj;

src/Components/benchmarkapps/Wasm.Performance/Driver/BenchmarkMeasurement.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ internal class BenchmarkMeasurement
99
{
1010
public DateTime Timestamp { get; internal set; }
1111
public string Name { get; internal set; }
12-
public double Value { get; internal set; }
12+
public object Value { get; internal set; }
1313
}
1414
}

src/Components/benchmarkapps/Wasm.Performance/Driver/Program.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.IO;
77
using System.IO.Compression;
88
using System.Linq;
9+
using System.Reflection;
910
using System.Runtime.ExceptionServices;
1011
using System.Text.Encodings.Web;
1112
using System.Text.Json;
@@ -93,6 +94,24 @@ private static void FormatAsBenchmarksOutput(List<BenchmarkResult> results, (lon
9394
});
9495
}
9596

97+
// Information about the build that this was produced from
98+
output.Metadata.Add(new BenchmarkMetadata
99+
{
100+
Source = "BlazorWasm",
101+
Name = "blazorwasm/commit",
102+
ShortDescription = "Commit Hash",
103+
});
104+
105+
output.Measurements.Add(new BenchmarkMeasurement
106+
{
107+
Timestamp = DateTime.UtcNow,
108+
Name = "blazorwasm/commit",
109+
Value = typeof(Program).Assembly
110+
.GetCustomAttributes<AssemblyMetadataAttribute>()
111+
.FirstOrDefault(f => f.Key == "CommitHash")
112+
?.Value,
113+
});
114+
96115
// Statistics about publish sizes
97116
output.Metadata.Add(new BenchmarkMetadata
98117
{
@@ -238,7 +257,7 @@ static async Task<long> GetBrotliCompressedSize(DirectoryInfo directory)
238257
if (!directory.Exists)
239258
{
240259
return 0;
241-
}
260+
}
242261

243262
var tasks = new List<Task<long>>();
244263
foreach (var item in directory.EnumerateFileSystemInfos())

src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<!-- Intentionally pinned this to .NET Core 3.1 since that's the supported version in the docker image -->
5-
<TargetFramework>netcoreapp3.1</TargetFramework>
4+
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
65

76
<UseLatestAspNetCoreReference>true</UseLatestAspNetCoreReference>
87
<OutputType>exe</OutputType>

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

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,8 @@
2424
}
2525

2626
@code {
27-
static string[] Clearances = new[] { "Alpha", "Beta", "Gamma", "Delta", "Epsilon" };
28-
Person smallOrgChart = GenerateOrgChart(1, 4);
29-
Person largeOrgChart = GenerateOrgChart(5, 4);
27+
Person smallOrgChart = Person.GenerateOrgChart(1, 4);
28+
Person largeOrgChart = Person.GenerateOrgChart(5, 4);
3029
string smallOrgChartJson;
3130
string largeOrgChartJson;
3231
int numPeopleDeserialized;
@@ -62,23 +61,6 @@
6261
void DeserializeLarge()
6362
=> numPeopleDeserialized = Deserialize(largeOrgChartJson);
6463

65-
static Person GenerateOrgChart(int totalDepth, int numDescendantsPerNode, int thisDepth = 0, string namePrefix = null, int siblingIndex = 0)
66-
{
67-
var name = $"{namePrefix ?? "CEO"} - Subordinate {siblingIndex}";
68-
var rng = new Random(0);
69-
return new Person
70-
{
71-
Name = name,
72-
IsAdmin = siblingIndex % 2 == 0,
73-
Salary = 10000000 / (thisDepth + 1),
74-
SecurityClearances = Clearances
75-
.ToDictionary(c => c, _ => (object)(rng.Next(0, 2) == 0)),
76-
Subordinates = Enumerable.Range(0, thisDepth < totalDepth ? numDescendantsPerNode : 0)
77-
.Select(index => GenerateOrgChart(totalDepth, numDescendantsPerNode, thisDepth + 1, name, index))
78-
.ToList()
79-
};
80-
}
81-
8264
static int Deserialize(string json)
8365
{
8466
var ceo = JsonSerializer.Deserialize<Person>(json);
@@ -87,13 +69,4 @@
8769

8870
static int CountPeople(Person root)
8971
=> 1 + (root.Subordinates?.Sum(CountPeople) ?? 0);
90-
91-
class Person
92-
{
93-
public string Name { get; set; }
94-
public int Salary { get; set; }
95-
public bool IsAdmin { get; set; }
96-
public List<Person> Subordinates { get; set; }
97-
public Dictionary<string, object> SecurityClearances { get; set; }
98-
}
9972
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
@page "/orgchart"
2+
@inject IJSRuntime JSRuntime
3+
4+
<h1>Org Chart</h1>
5+
<fieldset>
6+
<label>Depth: <input id="depth" type="number" @bind="depth" /></label>
7+
<label>Subordinates: <input id="subs" type="number" @bind="subs" /></label>
8+
9+
<button id="show" @onclick="Show">Show</button>
10+
<button id="hide" @onclick="Hide">Hide</button>
11+
</fieldset>
12+
13+
@if (show)
14+
{
15+
<PersonDisplay Person="Person.GenerateOrgChart(depth, subs)" />
16+
}
17+
18+
@code
19+
{
20+
int depth = 2;
21+
int subs = 5;
22+
bool show;
23+
24+
protected override void OnAfterRender(bool firstRender)
25+
{
26+
BenchmarkEvent.Send(JSRuntime, "Finished OrgChart rendering");
27+
}
28+
29+
void Hide()
30+
{
31+
show = false;
32+
}
33+
34+
void Show()
35+
{
36+
show = true;
37+
}
38+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
@inject IJSRuntime JSRuntime
2+
3+
<div class="person">
4+
<h2>
5+
@Person.Name
6+
@if (Person.IsAdmin)
7+
{
8+
<span>[Administrator]</span>
9+
}
10+
</h2>
11+
12+
Salary: $<h3 class="salary">@Person.Salary</h3>
13+
14+
<EditForm Model="Person">
15+
<div>
16+
<label>Salary</label>
17+
<InputNumber @bind-Value="Person.Salary" />
18+
</div>
19+
20+
<div>
21+
<label>Adminstrator: </label>
22+
<InputCheckbox @bind-Value="Person.IsAdmin" />
23+
</div>
24+
</EditForm>
25+
26+
<ul>
27+
@foreach (var kvp in Person.SecurityClearances)
28+
{
29+
<li>@kvp.Key: @kvp.Value</li>
30+
}
31+
</ul>
32+
</div>
33+
34+
35+
@foreach (var person in Person.Subordinates)
36+
{
37+
<ul>
38+
<li>
39+
<PersonDisplay Person="person" />
40+
</li>
41+
</ul>
42+
}
43+
44+
@code
45+
{
46+
[Parameter] public Person Person { get; set; }
47+
48+
protected override void OnAfterRender(bool firstRender)
49+
{
50+
BenchmarkEvent.Send(JSRuntime, "Finished PersonDisplay rendering");
51+
}
52+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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.Collections.Generic;
6+
using System.Linq;
7+
8+
namespace Wasm.Performance.TestApp
9+
{
10+
public class Person
11+
{
12+
static readonly string[] Clearances = new[] { "Alpha", "Beta", "Gamma", "Delta", "Epsilon" };
13+
14+
public string Name { get; set; }
15+
public int Salary { get; set; }
16+
public bool IsAdmin { get; set; }
17+
public List<Person> Subordinates { get; set; }
18+
public Dictionary<string, object> SecurityClearances { get; set; }
19+
20+
public static Person GenerateOrgChart(int totalDepth, int numDescendantsPerNode, int thisDepth = 0, string namePrefix = null, int siblingIndex = 0)
21+
{
22+
23+
var name = $"{namePrefix ?? "CEO"} - Subordinate {siblingIndex}";
24+
var rng = new Random(0);
25+
return new Person
26+
{
27+
Name = name,
28+
IsAdmin = siblingIndex % 2 == 0,
29+
Salary = 10000000 / (thisDepth + 1),
30+
SecurityClearances = Clearances
31+
.ToDictionary(c => c, _ => (object)(rng.Next(0, 2) == 0)),
32+
Subordinates = Enumerable.Range(0, thisDepth < totalDepth ? numDescendantsPerNode : 0)
33+
.Select(index => GenerateOrgChart(totalDepth, numDescendantsPerNode, thisDepth + 1, name, index))
34+
.ToList()
35+
};
36+
}
37+
}
38+
}

src/Components/benchmarkapps/Wasm.Performance/TestApp/Shared/MainLayout.razor

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
<a href="">Home</a> |
66
<a href="renderlist">RenderList</a> |
7-
<a href="json">JSON</a>
7+
<a href="json">JSON</a> |
8+
<a href="orgchart">OrgChart</a>
89

910
<hr/>
1011

src/Components/benchmarkapps/Wasm.Performance/TestApp/Wasm.Performance.TestApp.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
<PropertyGroup>
44
<TargetFramework>netstandard2.1</TargetFramework>
5-
<ReferenceBlazorBuildLocally>true</ReferenceBlazorBuildLocally>
65
<RazorLangVersion>3.0</RazorLangVersion>
6+
7+
<HasReferenceAssembly>false</HasReferenceAssembly>
8+
<IsProjectReferenceProvider>false</IsProjectReferenceProvider>
9+
<ReferenceBlazorBuildLocally>true</ReferenceBlazorBuildLocally>
710
</PropertyGroup>
811

912
<ItemGroup>

src/Components/benchmarkapps/Wasm.Performance/TestApp/_Imports.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@using System.Net.Http
22
@using Microsoft.AspNetCore.Components.Routing
3+
@using Microsoft.AspNetCore.Components.Forms
34
@using Microsoft.AspNetCore.Components.Web
45
@using Microsoft.JSInterop
56
@using Wasm.Performance.TestApp

src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { HtmlUI } from './lib/minibench/minibench.ui.js';
33
import './appStartup.js';
44
import './renderList.js';
55
import './jsonHandling.js';
6+
import './orgChart.js';
67

78
new HtmlUI('E2E Performance', '#display');
89

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { group, benchmark, setup, teardown } from './lib/minibench/minibench.js';
2+
import { BlazorApp } from './util/BlazorApp.js';
3+
import { receiveEvent } from './util/BenchmarkEvents.js';
4+
import { setInputValue } from './util/DOM.js';
5+
6+
group('Nested components', () => {
7+
let app;
8+
9+
setup(async () => {
10+
app = new BlazorApp();
11+
await app.start();
12+
app.navigateTo('orgChart');
13+
});
14+
15+
teardown(() => {
16+
app.dispose();
17+
});
18+
19+
benchmark('Render small nested component', () => measureOrgChart(app, 1, 4), {
20+
descriptor: {
21+
name: 'blazorwasm/orgchart-1-4-org',
22+
description: 'Time to render a complex component with small nesting (ms)'
23+
}
24+
});
25+
benchmark('Render large nested component', () => measureOrgChart(app, 3, 3), {
26+
descriptor: {
27+
name: 'blazorwasm/orgchart-3-3-org',
28+
description: 'Time to render a complex component with large nesting (ms)'
29+
}
30+
});
31+
benchmark('Render component with edit', () => measureOrgChartEdit(app, 3, 2), {
32+
descriptor: {
33+
name: 'blazorwasm/edit-orgchart-3-2',
34+
description: 'Time to peform updates in a nested component (ms)'
35+
}
36+
});
37+
});
38+
39+
async function measureOrgChart(app, depth, subs) {
40+
const appDocument = app.window.document;
41+
setInputValue(appDocument.querySelector('#depth'), depth.toString());
42+
setInputValue(appDocument.querySelector('#subs'), subs.toString());
43+
44+
let nextRenderCompletion = receiveEvent('Finished OrgChart rendering');
45+
appDocument.querySelector('#hide').click();
46+
await nextRenderCompletion;
47+
48+
if (appDocument.querySelectorAll('h2').length !== 0) {
49+
throw new Error('Wrong number of items rendered');
50+
}
51+
52+
nextRenderCompletion = receiveEvent('Finished OrgChart rendering');
53+
appDocument.querySelector('#show').click();
54+
await nextRenderCompletion;
55+
56+
if (appDocument.querySelectorAll('h2').length < depth * subs) {
57+
throw new Error('Wrong number of items rendered');
58+
}
59+
}
60+
61+
async function measureOrgChartEdit(app, depth, subs) {
62+
const appDocument = app.window.document;
63+
setInputValue(appDocument.querySelector('#depth'), depth.toString());
64+
setInputValue(appDocument.querySelector('#subs'), subs.toString());
65+
66+
let nextRenderCompletion = receiveEvent('Finished OrgChart rendering');
67+
appDocument.querySelector('#show').click();
68+
await nextRenderCompletion;
69+
70+
const elements = appDocument.querySelectorAll('.person');
71+
if (!elements) {
72+
throw new Error("No person elements found.");
73+
}
74+
75+
const personElement = elements.item(elements.length / 2);
76+
77+
const display = personElement.querySelector('.salary');
78+
const input = personElement.querySelector('input[type=number]');
79+
80+
nextRenderCompletion = receiveEvent('Finished PersonDisplay rendering');
81+
const updated = (Math.floor(Math.random() * 100000)).toString();
82+
setInputValue(input, updated);
83+
await nextRenderCompletion;
84+
85+
if (display.innerHTML != updated) {
86+
throw new Error('Value not updated after render');
87+
}
88+
}

src/Components/benchmarkapps/Wasm.Performance/dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ RUN git init \
2121
&& git reset --hard FETCH_HEAD \
2222
&& git submodule update --init
2323

24-
RUN dotnet publish -c Release -r linux-x64 -o /app ./src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj
24+
RUN ./restore.sh
25+
RUN .dotnet/dotnet publish -c Release -r linux-x64 -o /app ./src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj
2526
RUN chmod +x /app/Wasm.Performance.Driver
2627

2728
WORKDIR /app

0 commit comments

Comments
 (0)