-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
377 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
ClientBenchmarks.CreateClientBenchmark: DefaultJob | ||
Runtime = .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2; GC = Concurrent Workstation | ||
Mean = 19.798 ms, StdErr = 1.473 ms (7.44%), N = 100, StdDev = 14.735 ms | ||
Min = 0.363 ms, Q1 = 0.451 ms, Median = 32.087 ms, Q3 = 32.290 ms, Max = 32.615 ms | ||
IQR = 31.839 ms, LowerFence = -47.308 ms, UpperFence = 80.049 ms | ||
ConfidenceInterval = [14.801 ms; 24.795 ms] (CI 99.9%), Margin = 4.997 ms (25.24% of Mean) | ||
Skewness = -0.43, Kurtosis = 1.26, MValue = 3.19 | ||
-------------------- Histogram -------------------- | ||
[-0.503 ms ; 7.831 ms) | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | ||
[ 7.831 ms ; 14.505 ms) | @@ | ||
[14.505 ms ; 24.447 ms) | @@@@@ | ||
[24.447 ms ; 32.780 ms) | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | ||
--------------------------------------------------- | ||
|
||
ClientBenchmarks.CreateAndReadClientBenchmark: DefaultJob | ||
Runtime = .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2; GC = Concurrent Workstation | ||
Mean = 846.975 us, StdErr = 2.062 us (0.24%), N = 13, StdDev = 7.436 us | ||
Min = 834.617 us, Q1 = 842.163 us, Median = 843.979 us, Q3 = 853.402 us, Max = 858.243 us | ||
IQR = 11.239 us, LowerFence = 825.304 us, UpperFence = 870.260 us | ||
ConfidenceInterval = [838.070 us; 855.881 us] (CI 99.9%), Margin = 8.905 us (1.05% of Mean) | ||
Skewness = 0.11, Kurtosis = 1.68, MValue = 2 | ||
-------------------- Histogram -------------------- | ||
[830.466 us ; 862.394 us) | @@@@@@@@@@@@@ | ||
--------------------------------------------------- | ||
|
||
// * Summary * | ||
|
||
BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4112/23H2/2023Update/SunValley3) | ||
12th Gen Intel Core i7-12700F, 1 CPU, 20 logical and 12 physical cores | ||
.NET SDK 8.0.400 | ||
[Host] : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2 [AttachedDebugger] | ||
DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2 | ||
|
||
|
||
| Method | Mean | Error | StdDev | Median | | ||
|----------------------------- |------------:|------------:|-------------:|------------:| | ||
| CreateClientBenchmark | 19,797.9 us | 4,997.34 us | 14,734.78 us | 32,086.6 us | | ||
| CreateAndReadClientBenchmark | 847.0 us | 8.91 us | 7.44 us | 844.0 us | | ||
|
||
// * Warnings * | ||
MultimodalDistribution | ||
ClientBenchmarks.CreateClientBenchmark: Default -> It seems that the distribution can have several modes (mValue = 3.19) | ||
Environment | ||
Summary -> Benchmark was executed with attached debugger | ||
MinIterationTime | ||
ClientBenchmarks.CreateClientBenchmark: Default -> The minimum observed iteration time is 23.203ms which is very small. It's recommended to increase it to at least 100ms using more operations. | ||
|
||
// * Hints * | ||
Outliers | ||
ClientBenchmarks.CreateAndReadClientBenchmark: Default -> 2 outliers were removed (876.21 us, 883.15 us) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" /> | ||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> | ||
<PackageReference Include="Google.Protobuf" Version="3.28.0" /> | ||
<PackageReference Include="Grpc.Net.Client" Version="2.65.0" /> | ||
<PackageReference Include="Grpc.Net.Client.Web" Version="2.65.0" /> | ||
<Protobuf Include="..\..\src\Protos\webview.proto" GrpcServices="Client" Link="Protos\webview.proto" /> | ||
|
||
<ProjectReference Include="..\..\RemoteWebView\RemoteWebView.csproj" /> | ||
</ItemGroup> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,308 @@ | ||
using BenchmarkDotNet.Attributes; | ||
using BenchmarkDotNet.Configs; | ||
using BenchmarkDotNet.Running; | ||
using Google.Protobuf; | ||
using Grpc.Core; | ||
using Grpc.Net.Client; | ||
using Microsoft.Extensions.FileProviders; | ||
using PeakSWC.RemoteWebView; | ||
using System; | ||
using System.Diagnostics; | ||
using System.Text; | ||
|
||
namespace ClientBenchmark | ||
{ | ||
public class ClientBenchmarks | ||
{ | ||
private string _testGuid; | ||
private string _testFilePath; | ||
private string _rootDirectory; | ||
private string _testFileName = "wwwroot/css/site.css"; | ||
private WebViewIPC.WebViewIPCClient _client; | ||
private BrowserIPC.BrowserIPCClient _browser; | ||
private string randomString; | ||
private HttpClient httpClient; | ||
private string URL = "https://localhost:5001"; | ||
private bool _prodServer = true; | ||
|
||
private static readonly char[] chars = | ||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray(); | ||
|
||
public static string GenerateRandomString(int length) | ||
{ | ||
StringBuilder result = new StringBuilder(length); | ||
Random random = new Random(); | ||
|
||
for (int i = 0; i < length; i++) | ||
{ | ||
result.Append(chars[random.Next(chars.Length)]); | ||
} | ||
|
||
return result.ToString(); | ||
} | ||
private void KillExistingProcesses(string processName) | ||
{ | ||
try | ||
{ | ||
foreach (var process in Process.GetProcessesByName(processName)) | ||
{ | ||
Console.WriteLine($"Killing process: {process.ProcessName} (ID: {process.Id})"); | ||
process.Kill(); | ||
process.WaitForExit(); // Optionally wait for the process to exit | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
Console.WriteLine($"Error killing process: {ex.Message}"); | ||
} | ||
} | ||
|
||
private async Task PollHttpRequest(HttpClient httpClient, string url) | ||
{ | ||
bool serverStarted = false; | ||
while (!serverStarted) | ||
{ | ||
try | ||
{ | ||
var response = await httpClient.GetAsync(url); | ||
serverStarted = response.IsSuccessStatusCode; | ||
} | ||
catch | ||
{ | ||
// Server is not ready yet, wait a bit before retrying | ||
await Task.Delay(10); | ||
} | ||
} | ||
} | ||
|
||
|
||
[GlobalSetup] | ||
public void Setup() | ||
|
||
{ | ||
if (_prodServer) | ||
KillExistingProcesses("RemoteWebViewService"); | ||
|
||
var processStartInfo = new ProcessStartInfo | ||
{ | ||
#if DEBUG | ||
FileName = @"..\..\..\..\..\RemoteWebViewService\bin\publishNoAuth\RemoteWebViewService.exe", | ||
#else | ||
FileName = @"..\..\..\..\..\..\..\..\..\RemoteWebViewService\bin\publishNoAuth\RemoteWebViewService.exe", | ||
#endif | ||
RedirectStandardOutput = true | ||
}; | ||
|
||
var handler = new SocketsHttpHandler | ||
{ | ||
PooledConnectionIdleTimeout = Timeout.InfiniteTimeSpan, | ||
KeepAlivePingDelay = TimeSpan.FromSeconds(90), | ||
KeepAlivePingTimeout = TimeSpan.FromSeconds(60), | ||
EnableMultipleHttp2Connections = true | ||
}; | ||
|
||
|
||
httpClient = new HttpClient(handler); | ||
|
||
if (_prodServer) | ||
Process.Start(processStartInfo); | ||
|
||
PollHttpRequest(httpClient, URL).Wait(); | ||
|
||
_rootDirectory = Directory.CreateTempSubdirectory().FullName; | ||
_testFilePath = Path.Combine(_rootDirectory, _testFileName); // Example path | ||
|
||
// Create the directory if it doesn't exist | ||
Directory.CreateDirectory(Path.GetDirectoryName(_testFilePath)!); | ||
|
||
randomString = GenerateRandomString(102400); | ||
File.WriteAllText(_testFilePath, randomString); | ||
|
||
var channel = GrpcChannel.ForAddress(URL, new GrpcChannelOptions | ||
{ | ||
HttpClient = httpClient | ||
}); | ||
|
||
_client = new WebViewIPC.WebViewIPCClient(channel); | ||
_browser = new BrowserIPC.BrowserIPCClient(channel); | ||
} | ||
// [Benchmark] | ||
public void CreateClientBenchmark() | ||
{ | ||
string id = Guid.NewGuid().ToString(); | ||
var response = _client.CreateWebView(new CreateWebViewRequest { Id = id }); | ||
_client.Shutdown(new IdMessageRequest { Id = id }); | ||
} | ||
|
||
//[Benchmark] | ||
public void CreateAndReadClientBenchmark() | ||
{ | ||
string id = Guid.NewGuid().ToString(); | ||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); // shutdown waiting 20 seconds for tasks to cancel | ||
var response = _client.CreateWebView(new CreateWebViewRequest { Id = id }); | ||
// foreach (var message in response.ResponseStream.ReadAllAsync(cts.Token).ToBlockingEnumerable()) | ||
foreach (var message in response.ResponseStream.ReadAllAsync().ToBlockingEnumerable()) | ||
{ | ||
if (message.Response == "created:") | ||
{ | ||
_client.Shutdown(new IdMessageRequest { Id = id }); | ||
break; | ||
} | ||
|
||
break; | ||
} | ||
} | ||
|
||
// | CreateAndReadBrowserClientBenchmark | 313.2 ms | 6.08 ms | 9.10 ms | 512 byte message | ||
// CreateAndReadBrowserClientBenchmark | 317.7 ms | 4.29 ms | 4.01 ms | 1024 byte message | ||
// | CreateAndReadBrowserClientBenchmark | 310.8 ms | 4.23 ms | 3.95 ms | 1024 | ||
// | CreateAndReadBrowserClientBenchmark | 358.1 ms | 2.80 ms | 2.62 ms | 1000 messages x 10240 bytes | ||
// | CreateAndReadBrowserClientBenchmark | 359.1 ms | 3.83 ms | 3.59 ms | 1000 messages x 10240 bytes | ||
//[Benchmark] | ||
public void CreateAndReadBrowserClientBenchmark() | ||
{ | ||
int count = 0; | ||
int max = 1000; | ||
string id = Guid.NewGuid().ToString(); | ||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); // shutdown waiting 20 seconds for tasks to cancel | ||
var response = _client.CreateWebView(new CreateWebViewRequest { Id = id }); | ||
foreach (var message in response.ResponseStream.ReadAllAsync(cts.Token).ToBlockingEnumerable()) | ||
//foreach (var message in response.ResponseStream.ReadAllAsync().ToBlockingEnumerable()) | ||
{ | ||
if (message.Response == "created:") | ||
{ | ||
_ = Task.Run(() => { | ||
for (int i = 1; i <= max; i++) | ||
_browser.SendMessage(new SendSequenceMessageRequest { ClientId = id, Id=id, Sequence = (uint)i, Message = $"Message {i} {randomString}", IsPrimary=true, Url="url", Cookies="" }); | ||
} ); | ||
|
||
} | ||
else if (message.Response.StartsWith("Message")) | ||
{ | ||
count++; | ||
if (int.Parse (message.Response.Split(" ")[1]) == max) | ||
{ | ||
_client.Shutdown(new IdMessageRequest { Id = id }); | ||
break; | ||
} | ||
|
||
} | ||
} | ||
Console.WriteLine("Read " + count.ToString() + " messages"); | ||
} | ||
|
||
|
||
// | ReadFilesClientBenchmark | 677.8 ms | 13.48 ms | 13.85 ms | 1000 files of len | ||
// | ReadFilesClientBenchmark | 757.7 ms | 14.05 ms | 14.43 ms | 1000 files of len 10240 | ||
// | ReadFilesClientBenchmark | 736.5 ms | 12.93 ms | 12.09 ms | 1000 files of len 10240 | ||
// | ReadFilesClientBenchmark | 1.213 s | 0.0240 s | 0.0329 s | 1000 files of len 102400 | ||
[Benchmark] | ||
public void ReadFilesClientBenchmark() | ||
{ | ||
int count = 0; | ||
int max = 1000; | ||
string id = Guid.NewGuid().ToString(); | ||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30000)); // shutdown waiting 20 seconds for tasks to cancel | ||
var response = _client.CreateWebView(new CreateWebViewRequest { Id = id, EnableMirrors=false, HtmlHostPath="wwwroot" }); | ||
var bytes = 0; | ||
foreach (var message in response.ResponseStream.ReadAllAsync(cts.Token).ToBlockingEnumerable()) | ||
{ | ||
if (message.Response == "created:") | ||
{ | ||
AttachFileReader(cts, id, new PhysicalFileProvider(_rootDirectory+"/wwwroot")); | ||
string url = $"{URL}/{id}/{_testFileName}"; | ||
for (int i = 1; i <= max; i++) { | ||
var data = httpClient.GetStringAsync(url).Result; | ||
bytes += data.Length; | ||
//Console.WriteLine(data); | ||
count++; | ||
} | ||
|
||
_client.Shutdown(new IdMessageRequest { Id = id }); | ||
} | ||
|
||
} | ||
Console.WriteLine($"Read {count} files total bytes {bytes}"); | ||
} | ||
|
||
private void AttachFileReader(CancellationTokenSource cts, string id, IFileProvider fileProvider) | ||
{ | ||
_ = Task.Factory.StartNew(async () => | ||
{ | ||
var files = _client.FileReader(); | ||
try | ||
{ | ||
await files.RequestStream.WriteAsync(new FileReadRequest { Id = id, Init = new() }); | ||
|
||
await foreach (var message in files.ResponseStream.ReadAllAsync(cts.Token)) | ||
{ | ||
try | ||
{ | ||
var path = message.Path[(message.Path.IndexOf('/') + 1)..]; | ||
|
||
await files.RequestStream.WriteAsync(new FileReadRequest { Id = id, Length = new FileReadLengthRequest { Path = message.Path, Length = fileProvider.GetFileInfo(path).Length } }); | ||
|
||
using var stream = fileProvider.GetFileInfo(path).CreateReadStream() ?? null; | ||
if (stream == null) | ||
await files.RequestStream.WriteAsync(new FileReadRequest { Id = id, Data = new FileReadDataRequest { Path = message.Path, Data = ByteString.Empty } }); | ||
else | ||
{ | ||
var buffer = new Byte[8 * 1024]; | ||
int bytesRead = 0; | ||
|
||
while ((bytesRead = await stream.ReadAsync(buffer)) > 0) | ||
{ | ||
ByteString bs = ByteString.CopyFrom(buffer, 0, bytesRead); | ||
await files.RequestStream.WriteAsync(new FileReadRequest { Id = id, Data = new FileReadDataRequest { Path = message.Path, Data = bs } }); | ||
} | ||
await files.RequestStream.WriteAsync(new FileReadRequest { Id = id, Data = new FileReadDataRequest { Path = message.Path, Data = ByteString.Empty } }); | ||
} | ||
|
||
} | ||
catch (FileNotFoundException) | ||
{ | ||
Console.WriteLine("FileNotFoundException"); | ||
// TODO Warning to user? | ||
await files.RequestStream.WriteAsync(new FileReadRequest { Id = id, Data = new FileReadDataRequest { Path = message.Path, Data = ByteString.Empty } }); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Console.WriteLine(ex.ToString()); | ||
//FireDisconnected(ex); | ||
await files.RequestStream.WriteAsync(new FileReadRequest { Id = id, Data = new FileReadDataRequest { Path = message.Path, Data = ByteString.Empty } }); | ||
} | ||
} | ||
Console.WriteLine("Done reading files"); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Console.WriteLine(ex.ToString()); | ||
//FireDisconnected(ex); | ||
} | ||
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); | ||
} | ||
|
||
[GlobalCleanup] | ||
public void Cleanup() | ||
{ | ||
try | ||
{ | ||
// Clean up any temporary files created | ||
File.Delete(_testFilePath); | ||
Directory.Delete(_testFilePath); | ||
} | ||
catch { } | ||
} | ||
|
||
#if DEBUG | ||
static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(ClientBenchmarks).Assembly).Run(args, new DebugInProcessConfig()); | ||
#else | ||
public static void Main(string[] args) | ||
{ | ||
var summary = BenchmarkRunner.Run<ClientBenchmarks>(); | ||
Console.WriteLine(summary); | ||
Console.ReadLine(); | ||
} | ||
#endif | ||
} | ||
} |