Skip to content

Commit f4446f3

Browse files
authored
Use browser APIs to calculate Blazor's download size (#19547)
1 parent e15e1c2 commit f4446f3

File tree

9 files changed

+128
-163
lines changed

9 files changed

+128
-163
lines changed
Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,13 @@
1+
using System.Collections.Generic;
2+
13
namespace Wasm.Performance.Driver
24
{
35
class BenchmarkResult
46
{
5-
public string Name { get; set; }
6-
7-
public BenchmarkDescriptor Descriptor { get; set; }
8-
9-
public string ShortDescription { get; set; }
10-
11-
public bool Success { get; set; }
12-
13-
public int NumExecutions { get; set; }
14-
15-
public double Duration { get; set; }
16-
17-
public class BenchmarkDescriptor
18-
{
19-
public string Name { get; set; }
7+
/// <summary>The result of executing scenario benchmarks</summary>
8+
public List<BenchmarkScenarioResult> ScenarioResults { get; set; }
209

21-
public string Description { get; set; }
22-
}
10+
/// <summary>Downloaded application size in bytes</summary>
11+
public long DownloadSize { get; set; }
2312
}
2413
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
using System.Collections.Generic;
54
using System.Text.Json;
65
using Microsoft.AspNetCore.Builder;
76
using Microsoft.AspNetCore.Hosting;
@@ -25,7 +24,7 @@ public void Configure(IApplicationBuilder app)
2524

2625
app.Run(async context =>
2726
{
28-
var result = await JsonSerializer.DeserializeAsync<List<BenchmarkResult>>(context.Request.Body, new JsonSerializerOptions
27+
var result = await JsonSerializer.DeserializeAsync<BenchmarkResult>(context.Request.Body, new JsonSerializerOptions
2928
{
3029
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
3130
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace Wasm.Performance.Driver
2+
{
3+
class BenchmarkScenarioResult
4+
{
5+
public string Name { get; set; }
6+
7+
public BenchmarkDescriptor Descriptor { get; set; }
8+
9+
public string ShortDescription { get; set; }
10+
11+
public bool Success { get; set; }
12+
13+
public int NumExecutions { get; set; }
14+
15+
public double Duration { get; set; }
16+
17+
public class BenchmarkDescriptor
18+
{
19+
public string Name { get; set; }
20+
21+
public string Description { get; set; }
22+
}
23+
}
24+
}

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

Lines changed: 40 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ namespace Wasm.Performance.Driver
2424
public class Program
2525
{
2626
static readonly TimeSpan Timeout = TimeSpan.FromMinutes(3);
27-
static TaskCompletionSource<List<BenchmarkResult>> benchmarkResult = new TaskCompletionSource<List<BenchmarkResult>>();
27+
static TaskCompletionSource<BenchmarkResult> benchmarkResult = new TaskCompletionSource<BenchmarkResult>();
2828

2929
public static async Task<int> Main(string[] args)
3030
{
@@ -57,42 +57,36 @@ public static async Task<int> Main(string[] args)
5757
browser.Url = launchUrl;
5858
browser.Navigate();
5959

60-
var appSize = GetBlazorAppSize();
61-
await Task.WhenAll(benchmarkResult.Task, appSize);
62-
FormatAsBenchmarksOutput(benchmarkResult.Task.Result, appSize.Result);
60+
FormatAsBenchmarksOutput(benchmarkResult.Task.Result);
6361

6462
Console.WriteLine("Done executing benchmark");
6563
return 0;
6664
}
6765

68-
internal static void SetBenchmarkResult(List<BenchmarkResult> result)
66+
internal static void SetBenchmarkResult(BenchmarkResult result)
6967
{
7068
benchmarkResult.TrySetResult(result);
7169
}
7270

73-
private static void FormatAsBenchmarksOutput(List<BenchmarkResult> results, (long publishSize, long compressedSize) sizes)
71+
private static void FormatAsBenchmarksOutput(BenchmarkResult benchmarkResult)
7472
{
7573
// Sample of the the format: https://github.com/aspnet/Benchmarks/blob/e55f9e0312a7dd019d1268c1a547d1863f0c7237/src/Benchmarks/Program.cs#L51-L67
7674
var output = new BenchmarkOutput();
77-
foreach (var result in results)
75+
output.Metadata.Add(new BenchmarkMetadata
7876
{
79-
var scenarioName = result.Descriptor.Name;
80-
output.Metadata.Add(new BenchmarkMetadata
81-
{
82-
Source = "BlazorWasm",
83-
Name = scenarioName,
84-
ShortDescription = result.Name,
85-
LongDescription = result.Descriptor.Description,
86-
Format = "n2"
87-
});
77+
Source = "BlazorWasm",
78+
Name = "blazorwasm/download-size",
79+
ShortDescription = "Download size (KB)",
80+
LongDescription = "Download size (KB)",
81+
Format = "n2",
82+
});
8883

89-
output.Measurements.Add(new BenchmarkMeasurement
90-
{
91-
Timestamp = DateTime.UtcNow,
92-
Name = scenarioName,
93-
Value = result.Duration,
94-
});
95-
}
84+
output.Measurements.Add(new BenchmarkMeasurement
85+
{
86+
Timestamp = DateTime.UtcNow,
87+
Name = "blazorwasm/download-size",
88+
Value = ((float)benchmarkResult.DownloadSize) / 1024,
89+
});
9690

9791
// Information about the build that this was produced from
9892
output.Metadata.Add(new BenchmarkMetadata
@@ -112,38 +106,25 @@ private static void FormatAsBenchmarksOutput(List<BenchmarkResult> results, (lon
112106
?.Value,
113107
});
114108

115-
// Statistics about publish sizes
116-
output.Metadata.Add(new BenchmarkMetadata
117-
{
118-
Source = "BlazorWasm",
119-
Name = "blazorwasm/publish-size",
120-
ShortDescription = "Publish size (KB)",
121-
LongDescription = "Publish size (KB)",
122-
Format = "n2",
123-
});
124-
125-
output.Measurements.Add(new BenchmarkMeasurement
126-
{
127-
Timestamp = DateTime.UtcNow,
128-
Name = "blazorwasm/publish-size",
129-
Value = sizes.publishSize / 1024,
130-
});
131-
132-
output.Metadata.Add(new BenchmarkMetadata
109+
foreach (var result in benchmarkResult.ScenarioResults)
133110
{
134-
Source = "BlazorWasm",
135-
Name = "blazorwasm/compressed-publish-size",
136-
ShortDescription = "Publish size compressed app (KB)",
137-
LongDescription = "Publish size - compressed app (KB)",
138-
Format = "n2",
139-
});
111+
var scenarioName = result.Descriptor.Name;
112+
output.Metadata.Add(new BenchmarkMetadata
113+
{
114+
Source = "BlazorWasm",
115+
Name = scenarioName,
116+
ShortDescription = result.Name,
117+
LongDescription = result.Descriptor.Description,
118+
Format = "n2"
119+
});
140120

141-
output.Measurements.Add(new BenchmarkMeasurement
142-
{
143-
Timestamp = DateTime.UtcNow,
144-
Name = "blazorwasm/compressed-publish-size",
145-
Value = sizes.compressedSize / 1024,
146-
});
121+
output.Measurements.Add(new BenchmarkMeasurement
122+
{
123+
Timestamp = DateTime.UtcNow,
124+
Name = scenarioName,
125+
Value = result.Duration,
126+
});
127+
}
147128

148129
Console.WriteLine("#StartJobStatistics");
149130
Console.WriteLine(JsonSerializer.Serialize(output));
@@ -156,6 +137,12 @@ static IHost StartTestApp()
156137
{
157138
"--urls", "http://127.0.0.1:0",
158139
"--applicationpath", typeof(TestApp.Program).Assembly.Location,
140+
#if DEBUG
141+
"--contentroot",
142+
Path.GetFullPath(typeof(Program).Assembly.GetCustomAttributes<AssemblyMetadataAttribute>()
143+
.First(f => f.Key == "TestAppLocatiion")
144+
.Value)
145+
#endif
159146
};
160147

161148
var host = DevHostServerProgram.BuildWebHost(args);
@@ -216,76 +203,5 @@ static string GetListeningUrl(IHost testApp)
216203
.Addresses
217204
.First();
218205
}
219-
220-
static async Task<(long size, long compressedSize)> GetBlazorAppSize()
221-
{
222-
var testAssembly = typeof(TestApp.Program).Assembly;
223-
var testAssemblyLocation = new FileInfo(testAssembly.Location);
224-
var testApp = new DirectoryInfo(Path.Combine(
225-
testAssemblyLocation.Directory.FullName,
226-
testAssembly.GetName().Name));
227-
228-
return (GetDirectorySize(testApp), await GetBrotliCompressedSize(testApp));
229-
}
230-
231-
static long GetDirectorySize(DirectoryInfo directory)
232-
{
233-
// This can happen if you run the app without publishing it.
234-
if (!directory.Exists)
235-
{
236-
return 0;
237-
}
238-
239-
long size = 0;
240-
foreach (var item in directory.EnumerateFileSystemInfos())
241-
{
242-
if (item is FileInfo fileInfo)
243-
{
244-
size += fileInfo.Length;
245-
}
246-
else if (item is DirectoryInfo directoryInfo)
247-
{
248-
size += GetDirectorySize(directoryInfo);
249-
}
250-
}
251-
252-
return size;
253-
}
254-
255-
static async Task<long> GetBrotliCompressedSize(DirectoryInfo directory)
256-
{
257-
if (!directory.Exists)
258-
{
259-
return 0;
260-
}
261-
262-
var tasks = new List<Task<long>>();
263-
foreach (var item in directory.EnumerateFileSystemInfos())
264-
{
265-
if (item is FileInfo fileInfo)
266-
{
267-
tasks.Add(GetCompressedFileSize(fileInfo));
268-
}
269-
else if (item is DirectoryInfo directoryInfo)
270-
{
271-
tasks.Add(GetBrotliCompressedSize(directoryInfo));
272-
}
273-
}
274-
275-
return (await Task.WhenAll(tasks)).Sum(s => s);
276-
277-
async Task<long> GetCompressedFileSize(FileInfo fileInfo)
278-
{
279-
using var inputStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, 1, useAsync: true);
280-
using var outputStream = new MemoryStream();
281-
282-
using (var brotliStream = new BrotliStream(outputStream, CompressionLevel.Optimal, leaveOpen: true))
283-
{
284-
await inputStream.CopyToAsync(brotliStream);
285-
}
286-
287-
return outputStream.Length;
288-
}
289-
}
290206
}
291207
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk.Razor">
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
44
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
@@ -17,4 +17,14 @@
1717
<ProjectReference Include="..\TestApp\Wasm.Performance.TestApp.csproj" />
1818
</ItemGroup>
1919

20+
<Target Name="_AddTestProjectMetadataAttributes" BeforeTargets="BeforeCompile">
21+
<ItemGroup>
22+
<AssemblyAttribute
23+
Include="System.Reflection.AssemblyMetadataAttribute">
24+
<_Parameter1>TestAppLocatiion</_Parameter1>
25+
<_Parameter2>$(MSBuildThisFileDirectory)..\TestApp\</_Parameter2>
26+
</AssemblyAttribute>
27+
</ItemGroup>
28+
</Target>
29+
2030
</Project>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ Hello, world!
66
@code {
77
protected override void OnAfterRender(bool firstRender)
88
{
9-
BenchmarkEvent.Send(JSRuntime, "Rendered index.cshtml");
9+
BenchmarkEvent.Send(JSRuntime, "Rendered Index.razor");
1010
}
1111
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { BlazorApp } from './util/BlazorApp.js';
2+
3+
export async function getBlazorDownloadSize() {
4+
// Clear caches
5+
for (var key of await caches.keys()) {
6+
await caches.delete(key);
7+
}
8+
9+
const app = new BlazorApp();
10+
try {
11+
await app.start();
12+
const downloadSize = app.window.performance.getEntries().reduce((prev, next) => (next.encodedBodySize || 0) + prev, 0);
13+
return downloadSize;
14+
} finally {
15+
app.dispose();
16+
}
17+
}

0 commit comments

Comments
 (0)