From f4446f373feccc8754e59d205ed563b2b0b0d8bb Mon Sep 17 00:00:00 2001 From: Pranav K Date: Thu, 5 Mar 2020 06:38:35 -0800 Subject: [PATCH] Use browser APIs to calculate Blazor's download size (#19547) --- .../Driver/BenchmarkResult.cs | 23 +-- .../Driver/BenchmarkResultsStartup.cs | 3 +- .../Driver/BenchmarkScenarioResult.cs | 24 +++ .../Wasm.Performance/Driver/Program.cs | 164 +++++------------- .../Driver/Wasm.Performance.Driver.csproj | 12 +- .../TestApp/Pages/Index.razor | 2 +- .../wwwroot/benchmarks/blazorDownloadSize.js | 17 ++ .../TestApp/wwwroot/benchmarks/index.js | 44 +++-- .../wwwroot/benchmarks/util/BlazorApp.js | 2 +- 9 files changed, 128 insertions(+), 163 deletions(-) create mode 100644 src/Components/benchmarkapps/Wasm.Performance/Driver/BenchmarkScenarioResult.cs create mode 100644 src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/blazorDownloadSize.js diff --git a/src/Components/benchmarkapps/Wasm.Performance/Driver/BenchmarkResult.cs b/src/Components/benchmarkapps/Wasm.Performance/Driver/BenchmarkResult.cs index 33e4c4094b..badc3122f4 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/Driver/BenchmarkResult.cs +++ b/src/Components/benchmarkapps/Wasm.Performance/Driver/BenchmarkResult.cs @@ -1,24 +1,13 @@ +using System.Collections.Generic; + namespace Wasm.Performance.Driver { class BenchmarkResult { - public string Name { get; set; } + /// The result of executing scenario benchmarks + public List ScenarioResults { get; set; } - public BenchmarkDescriptor Descriptor { get; set; } - - public string ShortDescription { get; set; } - - public bool Success { get; set; } - - public int NumExecutions { get; set; } - - public double Duration { get; set; } - - public class BenchmarkDescriptor - { - public string Name { get; set; } - - public string Description { get; set; } - } + /// Downloaded application size in bytes + public long DownloadSize { get; set; } } } \ No newline at end of file diff --git a/src/Components/benchmarkapps/Wasm.Performance/Driver/BenchmarkResultsStartup.cs b/src/Components/benchmarkapps/Wasm.Performance/Driver/BenchmarkResultsStartup.cs index 7a4af028df..326530df8e 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/Driver/BenchmarkResultsStartup.cs +++ b/src/Components/benchmarkapps/Wasm.Performance/Driver/BenchmarkResultsStartup.cs @@ -1,7 +1,6 @@ // 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.Collections.Generic; using System.Text.Json; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -25,7 +24,7 @@ namespace Wasm.Performance.Driver app.Run(async context => { - var result = await JsonSerializer.DeserializeAsync>(context.Request.Body, new JsonSerializerOptions + var result = await JsonSerializer.DeserializeAsync(context.Request.Body, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }); diff --git a/src/Components/benchmarkapps/Wasm.Performance/Driver/BenchmarkScenarioResult.cs b/src/Components/benchmarkapps/Wasm.Performance/Driver/BenchmarkScenarioResult.cs new file mode 100644 index 0000000000..5311b1a4ad --- /dev/null +++ b/src/Components/benchmarkapps/Wasm.Performance/Driver/BenchmarkScenarioResult.cs @@ -0,0 +1,24 @@ +namespace Wasm.Performance.Driver +{ + class BenchmarkScenarioResult + { + public string Name { get; set; } + + public BenchmarkDescriptor Descriptor { get; set; } + + public string ShortDescription { get; set; } + + public bool Success { get; set; } + + public int NumExecutions { get; set; } + + public double Duration { get; set; } + + public class BenchmarkDescriptor + { + public string Name { get; set; } + + public string Description { get; set; } + } + } +} \ No newline at end of file diff --git a/src/Components/benchmarkapps/Wasm.Performance/Driver/Program.cs b/src/Components/benchmarkapps/Wasm.Performance/Driver/Program.cs index 6c9cd0dc78..1d291e1fb9 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/Driver/Program.cs +++ b/src/Components/benchmarkapps/Wasm.Performance/Driver/Program.cs @@ -24,7 +24,7 @@ namespace Wasm.Performance.Driver public class Program { static readonly TimeSpan Timeout = TimeSpan.FromMinutes(3); - static TaskCompletionSource> benchmarkResult = new TaskCompletionSource>(); + static TaskCompletionSource benchmarkResult = new TaskCompletionSource(); public static async Task Main(string[] args) { @@ -57,42 +57,36 @@ namespace Wasm.Performance.Driver browser.Url = launchUrl; browser.Navigate(); - var appSize = GetBlazorAppSize(); - await Task.WhenAll(benchmarkResult.Task, appSize); - FormatAsBenchmarksOutput(benchmarkResult.Task.Result, appSize.Result); + FormatAsBenchmarksOutput(benchmarkResult.Task.Result); Console.WriteLine("Done executing benchmark"); return 0; } - internal static void SetBenchmarkResult(List result) + internal static void SetBenchmarkResult(BenchmarkResult result) { benchmarkResult.TrySetResult(result); } - private static void FormatAsBenchmarksOutput(List results, (long publishSize, long compressedSize) sizes) + private static void FormatAsBenchmarksOutput(BenchmarkResult benchmarkResult) { // Sample of the the format: https://github.com/aspnet/Benchmarks/blob/e55f9e0312a7dd019d1268c1a547d1863f0c7237/src/Benchmarks/Program.cs#L51-L67 var output = new BenchmarkOutput(); - foreach (var result in results) + output.Metadata.Add(new BenchmarkMetadata { - var scenarioName = result.Descriptor.Name; - output.Metadata.Add(new BenchmarkMetadata - { - Source = "BlazorWasm", - Name = scenarioName, - ShortDescription = result.Name, - LongDescription = result.Descriptor.Description, - Format = "n2" - }); + Source = "BlazorWasm", + Name = "blazorwasm/download-size", + ShortDescription = "Download size (KB)", + LongDescription = "Download size (KB)", + Format = "n2", + }); - output.Measurements.Add(new BenchmarkMeasurement - { - Timestamp = DateTime.UtcNow, - Name = scenarioName, - Value = result.Duration, - }); - } + output.Measurements.Add(new BenchmarkMeasurement + { + Timestamp = DateTime.UtcNow, + Name = "blazorwasm/download-size", + Value = ((float)benchmarkResult.DownloadSize) / 1024, + }); // Information about the build that this was produced from output.Metadata.Add(new BenchmarkMetadata @@ -112,38 +106,25 @@ namespace Wasm.Performance.Driver ?.Value, }); - // Statistics about publish sizes - output.Metadata.Add(new BenchmarkMetadata + foreach (var result in benchmarkResult.ScenarioResults) { - Source = "BlazorWasm", - Name = "blazorwasm/publish-size", - ShortDescription = "Publish size (KB)", - LongDescription = "Publish size (KB)", - Format = "n2", - }); + var scenarioName = result.Descriptor.Name; + output.Metadata.Add(new BenchmarkMetadata + { + Source = "BlazorWasm", + Name = scenarioName, + ShortDescription = result.Name, + LongDescription = result.Descriptor.Description, + Format = "n2" + }); - output.Measurements.Add(new BenchmarkMeasurement - { - Timestamp = DateTime.UtcNow, - Name = "blazorwasm/publish-size", - Value = sizes.publishSize / 1024, - }); - - output.Metadata.Add(new BenchmarkMetadata - { - Source = "BlazorWasm", - Name = "blazorwasm/compressed-publish-size", - ShortDescription = "Publish size compressed app (KB)", - LongDescription = "Publish size - compressed app (KB)", - Format = "n2", - }); - - output.Measurements.Add(new BenchmarkMeasurement - { - Timestamp = DateTime.UtcNow, - Name = "blazorwasm/compressed-publish-size", - Value = sizes.compressedSize / 1024, - }); + output.Measurements.Add(new BenchmarkMeasurement + { + Timestamp = DateTime.UtcNow, + Name = scenarioName, + Value = result.Duration, + }); + } Console.WriteLine("#StartJobStatistics"); Console.WriteLine(JsonSerializer.Serialize(output)); @@ -156,6 +137,12 @@ namespace Wasm.Performance.Driver { "--urls", "http://127.0.0.1:0", "--applicationpath", typeof(TestApp.Program).Assembly.Location, +#if DEBUG + "--contentroot", + Path.GetFullPath(typeof(Program).Assembly.GetCustomAttributes() + .First(f => f.Key == "TestAppLocatiion") + .Value) +#endif }; var host = DevHostServerProgram.BuildWebHost(args); @@ -216,76 +203,5 @@ namespace Wasm.Performance.Driver .Addresses .First(); } - - static async Task<(long size, long compressedSize)> GetBlazorAppSize() - { - var testAssembly = typeof(TestApp.Program).Assembly; - var testAssemblyLocation = new FileInfo(testAssembly.Location); - var testApp = new DirectoryInfo(Path.Combine( - testAssemblyLocation.Directory.FullName, - testAssembly.GetName().Name)); - - return (GetDirectorySize(testApp), await GetBrotliCompressedSize(testApp)); - } - - static long GetDirectorySize(DirectoryInfo directory) - { - // This can happen if you run the app without publishing it. - if (!directory.Exists) - { - return 0; - } - - long size = 0; - foreach (var item in directory.EnumerateFileSystemInfos()) - { - if (item is FileInfo fileInfo) - { - size += fileInfo.Length; - } - else if (item is DirectoryInfo directoryInfo) - { - size += GetDirectorySize(directoryInfo); - } - } - - return size; - } - - static async Task GetBrotliCompressedSize(DirectoryInfo directory) - { - if (!directory.Exists) - { - return 0; - } - - var tasks = new List>(); - foreach (var item in directory.EnumerateFileSystemInfos()) - { - if (item is FileInfo fileInfo) - { - tasks.Add(GetCompressedFileSize(fileInfo)); - } - else if (item is DirectoryInfo directoryInfo) - { - tasks.Add(GetBrotliCompressedSize(directoryInfo)); - } - } - - return (await Task.WhenAll(tasks)).Sum(s => s); - - async Task GetCompressedFileSize(FileInfo fileInfo) - { - using var inputStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, 1, useAsync: true); - using var outputStream = new MemoryStream(); - - using (var brotliStream = new BrotliStream(outputStream, CompressionLevel.Optimal, leaveOpen: true)) - { - await inputStream.CopyToAsync(brotliStream); - } - - return outputStream.Length; - } - } } } 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 bbb82342e6..487c261520 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj +++ b/src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj @@ -1,4 +1,4 @@ - + $(DefaultNetCoreTargetFramework) @@ -17,4 +17,14 @@ + + + + <_Parameter1>TestAppLocatiion + <_Parameter2>$(MSBuildThisFileDirectory)..\TestApp\ + + + + diff --git a/src/Components/benchmarkapps/Wasm.Performance/TestApp/Pages/Index.razor b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Pages/Index.razor index e740dc121c..9de3b230e4 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/TestApp/Pages/Index.razor +++ b/src/Components/benchmarkapps/Wasm.Performance/TestApp/Pages/Index.razor @@ -6,6 +6,6 @@ Hello, world! @code { protected override void OnAfterRender(bool firstRender) { - BenchmarkEvent.Send(JSRuntime, "Rendered index.cshtml"); + BenchmarkEvent.Send(JSRuntime, "Rendered Index.razor"); } } diff --git a/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/blazorDownloadSize.js b/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/blazorDownloadSize.js new file mode 100644 index 0000000000..8cbe6df1a6 --- /dev/null +++ b/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/blazorDownloadSize.js @@ -0,0 +1,17 @@ +import { BlazorApp } from './util/BlazorApp.js'; + +export async function getBlazorDownloadSize() { + // Clear caches + for (var key of await caches.keys()) { + await caches.delete(key); + } + + const app = new BlazorApp(); + try { + await app.start(); + const downloadSize = app.window.performance.getEntries().reduce((prev, next) => (next.encodedBodySize || 0) + prev, 0); + return downloadSize; + } finally { + app.dispose(); + } +} 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 860af58bd3..3fa33b6ae1 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/index.js +++ b/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/index.js @@ -4,37 +4,47 @@ import './appStartup.js'; import './renderList.js'; import './jsonHandling.js'; import './orgChart.js'; +import { getBlazorDownloadSize } from './blazorDownloadSize.js'; new HtmlUI('E2E Performance', '#display'); if (location.href.indexOf('#automated') !== -1) { - const query = new URLSearchParams(window.location.search); - const group = query.get('group'); - const resultsUrl = query.get('resultsUrl'); + (async function() { + const query = new URLSearchParams(window.location.search); + const group = query.get('group'); + const resultsUrl = query.get('resultsUrl'); - groups.filter(g => !group || g.name === group).forEach(g => g.runAll()); + console.log('Calculating download size...'); + const downloadSize = await getBlazorDownloadSize(); + console.log('Download size: ', downloadSize); - const benchmarksResults = []; - onBenchmarkEvent(async (status, args) => { - switch (status) { + const scenarioResults = []; + groups.filter(g => !group || g.name === group).forEach(g => g.runAll()); + + onBenchmarkEvent(async (status, args) => { + switch (status) { case BenchmarkEvent.runStarted: - benchmarksResults.length = 0; + scenarioResults.length = 0; break; case BenchmarkEvent.benchmarkCompleted: case BenchmarkEvent.benchmarkError: console.log(`Completed benchmark ${args.name}`); - benchmarksResults.push(args); + scenarioResults.push(args); break; case BenchmarkEvent.runCompleted: - if (resultsUrl) { - await fetch(resultsUrl, { - method: 'post', - body: JSON.stringify(benchmarksResults) - }); - } - break; + if (resultsUrl) { + await fetch(resultsUrl, { + method: 'post', + body: JSON.stringify({ + downloadSize: downloadSize, + scenarioResults: scenarioResults + }) + }); + } + break; default: throw new Error(`Unknown status: ${status}`); } - }) + }); + })(); } diff --git a/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/util/BlazorApp.js b/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/util/BlazorApp.js index d3211c50db..e4d798115f 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/util/BlazorApp.js +++ b/src/Components/benchmarkapps/Wasm.Performance/TestApp/wwwroot/benchmarks/util/BlazorApp.js @@ -12,7 +12,7 @@ export class BlazorApp { async start() { this._frame.src = 'blazor-frame.html'; - await receiveEvent('Rendered index.cshtml'); + await receiveEvent('Rendered Index.razor'); } navigateTo(url) {