diff --git a/eng/Build.props b/eng/Build.props index 64ae3afd52..712c0c61d4 100644 --- a/eng/Build.props +++ b/eng/Build.props @@ -43,7 +43,7 @@ () + .FirstOrDefault(f => f.Key == "CommitHash") + ?.Value, + }); + // Statistics about publish sizes output.Metadata.Add(new BenchmarkMetadata { @@ -238,7 +257,7 @@ namespace Wasm.Performance.Driver if (!directory.Exists) { return 0; - } + } var tasks = new List>(); foreach (var item in directory.EnumerateFileSystemInfos()) diff --git a/src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj b/src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj index 78c98d79eb..ed93c75801 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj +++ b/src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj @@ -1,8 +1,7 @@ - - netcoreapp3.1 + $(DefaultNetCoreTargetFramework) true exe diff --git a/src/Components/benchmarkapps/Wasm.Performance/TestApp/Pages/Json.razor b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Pages/Json.razor index 84722da2f1..1c0d37ea9d 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/TestApp/Pages/Json.razor +++ b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Pages/Json.razor @@ -24,9 +24,8 @@ } @code { - static string[] Clearances = new[] { "Alpha", "Beta", "Gamma", "Delta", "Epsilon" }; - Person smallOrgChart = GenerateOrgChart(1, 4); - Person largeOrgChart = GenerateOrgChart(5, 4); + Person smallOrgChart = Person.GenerateOrgChart(1, 4); + Person largeOrgChart = Person.GenerateOrgChart(5, 4); string smallOrgChartJson; string largeOrgChartJson; int numPeopleDeserialized; @@ -62,23 +61,6 @@ void DeserializeLarge() => numPeopleDeserialized = Deserialize(largeOrgChartJson); - static Person GenerateOrgChart(int totalDepth, int numDescendantsPerNode, int thisDepth = 0, string namePrefix = null, int siblingIndex = 0) - { - var name = $"{namePrefix ?? "CEO"} - Subordinate {siblingIndex}"; - var rng = new Random(0); - return new Person - { - Name = name, - IsAdmin = siblingIndex % 2 == 0, - Salary = 10000000 / (thisDepth + 1), - SecurityClearances = Clearances - .ToDictionary(c => c, _ => (object)(rng.Next(0, 2) == 0)), - Subordinates = Enumerable.Range(0, thisDepth < totalDepth ? numDescendantsPerNode : 0) - .Select(index => GenerateOrgChart(totalDepth, numDescendantsPerNode, thisDepth + 1, name, index)) - .ToList() - }; - } - static int Deserialize(string json) { var ceo = JsonSerializer.Deserialize(json); @@ -87,13 +69,4 @@ static int CountPeople(Person root) => 1 + (root.Subordinates?.Sum(CountPeople) ?? 0); - - class Person - { - public string Name { get; set; } - public int Salary { get; set; } - public bool IsAdmin { get; set; } - public List Subordinates { get; set; } - public Dictionary SecurityClearances { get; set; } - } } diff --git a/src/Components/benchmarkapps/Wasm.Performance/TestApp/Pages/OrgChart.razor b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Pages/OrgChart.razor new file mode 100644 index 0000000000..c89854981a --- /dev/null +++ b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Pages/OrgChart.razor @@ -0,0 +1,38 @@ +@page "/orgchart" +@inject IJSRuntime JSRuntime + +

Org Chart

+
+ + + + + +
+ +@if (show) +{ + +} + +@code +{ + int depth = 2; + int subs = 5; + bool show; + + protected override void OnAfterRender(bool firstRender) + { + BenchmarkEvent.Send(JSRuntime, "Finished OrgChart rendering"); + } + + void Hide() + { + show = false; + } + + void Show() + { + show = true; + } +} \ No newline at end of file diff --git a/src/Components/benchmarkapps/Wasm.Performance/TestApp/Pages/PersonDisplay.razor b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Pages/PersonDisplay.razor new file mode 100644 index 0000000000..00e8cdddb3 --- /dev/null +++ b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Pages/PersonDisplay.razor @@ -0,0 +1,52 @@ +@inject IJSRuntime JSRuntime + +
+

+ @Person.Name + @if (Person.IsAdmin) + { + [Administrator] + } +

+ + Salary: $

@Person.Salary

+ + +
+ + +
+ +
+ + +
+
+ +
    + @foreach (var kvp in Person.SecurityClearances) + { +
  • @kvp.Key: @kvp.Value
  • + } +
+
+ + +@foreach (var person in Person.Subordinates) +{ +
    +
  • + +
  • +
+} + +@code +{ + [Parameter] public Person Person { get; set; } + + protected override void OnAfterRender(bool firstRender) + { + BenchmarkEvent.Send(JSRuntime, "Finished PersonDisplay rendering"); + } +} \ No newline at end of file diff --git a/src/Components/benchmarkapps/Wasm.Performance/TestApp/Person.cs b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Person.cs new file mode 100644 index 0000000000..ecaa7d23ec --- /dev/null +++ b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Person.cs @@ -0,0 +1,38 @@ +// 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.Collections.Generic; +using System.Linq; + +namespace Wasm.Performance.TestApp +{ + public class Person + { + static readonly string[] Clearances = new[] { "Alpha", "Beta", "Gamma", "Delta", "Epsilon" }; + + public string Name { get; set; } + public int Salary { get; set; } + public bool IsAdmin { get; set; } + public List Subordinates { get; set; } + public Dictionary SecurityClearances { get; set; } + + public static Person GenerateOrgChart(int totalDepth, int numDescendantsPerNode, int thisDepth = 0, string namePrefix = null, int siblingIndex = 0) + { + + var name = $"{namePrefix ?? "CEO"} - Subordinate {siblingIndex}"; + var rng = new Random(0); + return new Person + { + Name = name, + IsAdmin = siblingIndex % 2 == 0, + Salary = 10000000 / (thisDepth + 1), + SecurityClearances = Clearances + .ToDictionary(c => c, _ => (object)(rng.Next(0, 2) == 0)), + Subordinates = Enumerable.Range(0, thisDepth < totalDepth ? numDescendantsPerNode : 0) + .Select(index => GenerateOrgChart(totalDepth, numDescendantsPerNode, thisDepth + 1, name, index)) + .ToList() + }; + } + } +} \ No newline at end of file diff --git a/src/Components/benchmarkapps/Wasm.Performance/TestApp/Shared/MainLayout.razor b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Shared/MainLayout.razor index 259daf45d6..c171eaa32c 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/TestApp/Shared/MainLayout.razor +++ b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Shared/MainLayout.razor @@ -4,7 +4,8 @@ Home | RenderList | -JSON +JSON | +OrgChart
diff --git a/src/Components/benchmarkapps/Wasm.Performance/TestApp/Wasm.Performance.TestApp.csproj b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Wasm.Performance.TestApp.csproj index 5910061d8d..45467d6940 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/TestApp/Wasm.Performance.TestApp.csproj +++ b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Wasm.Performance.TestApp.csproj @@ -2,8 +2,11 @@ netstandard2.1 - true 3.0 + + false + false + true diff --git a/src/Components/benchmarkapps/Wasm.Performance/TestApp/_Imports.razor b/src/Components/benchmarkapps/Wasm.Performance/TestApp/_Imports.razor index fef56339a9..638e1ea028 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/TestApp/_Imports.razor +++ b/src/Components/benchmarkapps/Wasm.Performance/TestApp/_Imports.razor @@ -1,5 +1,6 @@ @using System.Net.Http @using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Web @using Microsoft.JSInterop @using Wasm.Performance.TestApp diff --git a/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/index.js b/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/index.js index c1690cfac8..860af58bd3 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/index.js +++ b/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/index.js @@ -3,6 +3,7 @@ import { HtmlUI } from './lib/minibench/minibench.ui.js'; import './appStartup.js'; import './renderList.js'; import './jsonHandling.js'; +import './orgChart.js'; new HtmlUI('E2E Performance', '#display'); diff --git a/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/orgChart.js b/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/orgChart.js new file mode 100644 index 0000000000..4021ad66f9 --- /dev/null +++ b/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/orgChart.js @@ -0,0 +1,88 @@ +import { group, benchmark, setup, teardown } from './lib/minibench/minibench.js'; +import { BlazorApp } from './util/BlazorApp.js'; +import { receiveEvent } from './util/BenchmarkEvents.js'; +import { setInputValue } from './util/DOM.js'; + +group('Nested components', () => { + let app; + + setup(async () => { + app = new BlazorApp(); + await app.start(); + app.navigateTo('orgChart'); + }); + + teardown(() => { + app.dispose(); + }); + + benchmark('Render small nested component', () => measureOrgChart(app, 1, 4), { + descriptor: { + name: 'blazorwasm/orgchart-1-4-org', + description: 'Time to render a complex component with small nesting (ms)' + } + }); + benchmark('Render large nested component', () => measureOrgChart(app, 3, 3), { + descriptor: { + name: 'blazorwasm/orgchart-3-3-org', + description: 'Time to render a complex component with large nesting (ms)' + } + }); + benchmark('Render component with edit', () => measureOrgChartEdit(app, 3, 2), { + descriptor: { + name: 'blazorwasm/edit-orgchart-3-2', + description: 'Time to peform updates in a nested component (ms)' + } + }); +}); + +async function measureOrgChart(app, depth, subs) { + const appDocument = app.window.document; + setInputValue(appDocument.querySelector('#depth'), depth.toString()); + setInputValue(appDocument.querySelector('#subs'), subs.toString()); + + let nextRenderCompletion = receiveEvent('Finished OrgChart rendering'); + appDocument.querySelector('#hide').click(); + await nextRenderCompletion; + + if (appDocument.querySelectorAll('h2').length !== 0) { + throw new Error('Wrong number of items rendered'); + } + + nextRenderCompletion = receiveEvent('Finished OrgChart rendering'); + appDocument.querySelector('#show').click(); + await nextRenderCompletion; + + if (appDocument.querySelectorAll('h2').length < depth * subs) { + throw new Error('Wrong number of items rendered'); + } +} + +async function measureOrgChartEdit(app, depth, subs) { + const appDocument = app.window.document; + setInputValue(appDocument.querySelector('#depth'), depth.toString()); + setInputValue(appDocument.querySelector('#subs'), subs.toString()); + + let nextRenderCompletion = receiveEvent('Finished OrgChart rendering'); + appDocument.querySelector('#show').click(); + await nextRenderCompletion; + + const elements = appDocument.querySelectorAll('.person'); + if (!elements) { + throw new Error("No person elements found."); + } + + const personElement = elements.item(elements.length / 2); + + const display = personElement.querySelector('.salary'); + const input = personElement.querySelector('input[type=number]'); + + nextRenderCompletion = receiveEvent('Finished PersonDisplay rendering'); + const updated = (Math.floor(Math.random() * 100000)).toString(); + setInputValue(input, updated); + await nextRenderCompletion; + + if (display.innerHTML != updated) { + throw new Error('Value not updated after render'); + } +} \ No newline at end of file diff --git a/src/Components/benchmarkapps/Wasm.Performance/dockerfile b/src/Components/benchmarkapps/Wasm.Performance/dockerfile index 69f27a9212..410556f21a 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/dockerfile +++ b/src/Components/benchmarkapps/Wasm.Performance/dockerfile @@ -21,7 +21,8 @@ RUN git init \ && git reset --hard FETCH_HEAD \ && git submodule update --init -RUN dotnet publish -c Release -r linux-x64 -o /app ./src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj +RUN ./restore.sh +RUN .dotnet/dotnet publish -c Release -r linux-x64 -o /app ./src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj RUN chmod +x /app/Wasm.Performance.Driver WORKDIR /app