Last active
January 27, 2022 23:42
-
-
Save benaadams/cd2969e13c582ab4070489b5c585024e to your computer and use it in GitHub Desktop.
Testing ValueTask pooling
This file contains hidden or 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
using System; | |
using System.Diagnostics; | |
using System.Net.Http; | |
using System.Threading; | |
using System.Threading.Tasks; | |
class Program | |
{ | |
const string api = "http://127.0.0.1:5001/"; | |
private readonly static HttpClient s_client = new HttpClient(); | |
private readonly static Memory<byte> s_memory = new Memory<byte>(new byte[1]); | |
const int WarmUpOps = 1000; | |
const int Delay = 500; | |
const int MainOps = 1_000_000; | |
static readonly Func<int, ParallelLoopState, int, int> s_action = HttpCallLoop; | |
static void Main(string[] args) | |
{ | |
ThreadPool.GetMinThreads(out var workerThreads, out int completionThreads); | |
ThreadPool.SetMinThreads(int.MaxValue, completionThreads); | |
Parallel.For( | |
0, | |
Environment.ProcessorCount / 2, | |
() => WarmUpOps / Math.Max(1, Environment.ProcessorCount / 2), | |
s_action, | |
(local) => { } | |
); | |
// Let tiered compliation kick in | |
Thread.Sleep(Delay); | |
Test(parallelism: 1); | |
Test(parallelism: 2); | |
Test(parallelism: 3); | |
Test(parallelism: 4); | |
} | |
private static void Test(int parallelism) | |
{ | |
var startBytes = GC.GetTotalAllocatedBytes(); | |
var sw = Stopwatch.StartNew(); | |
int parallelOps = MainOps / parallelism; | |
Parallel.For( | |
0, | |
parallelism, | |
() => parallelOps, | |
s_action, | |
(local) => { } | |
); | |
sw.Stop(); | |
var endBytes = GC.GetTotalAllocatedBytes(); | |
double totalOps = parallelOps * parallelism; | |
double rps = totalOps / sw.Elapsed.TotalSeconds; | |
Console.WriteLine($"Threads: {parallelism}, Request/s: {rps:N1}, Time: {sw.ElapsedMilliseconds:N0} ms, Allocated/Request: {((endBytes - startBytes)/totalOps):N0}"); | |
} | |
private static int HttpCallLoop(int n, ParallelLoopState state, int loops) | |
{ | |
AsyncCalls(loops).Wait(); | |
return loops; | |
} | |
private static async Task AsyncCalls(int loops) | |
{ | |
for (var i = 0; i < loops; i++) | |
{ | |
await ExecuteHttpClient(api, HttpMethod.Get); | |
} | |
} | |
private static async ValueTask ExecuteHttpClient(string url, HttpMethod httpMethod) | |
{ | |
using var req = new HttpRequestMessage(httpMethod, url); | |
using var httpResponseMessage = await s_client.SendAsync(req); | |
using var stream = await httpResponseMessage.Content.ReadAsStreamAsync(); | |
while (await stream.ReadAsync(s_memory) != 0) | |
{ | |
// Reading 1 byte at a time; 14 reads which is not unrepresentative | |
} | |
} | |
} |
This file contains hidden or 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
// 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.IO; | |
using System.IO.Pipelines; | |
using System.Net; | |
using System.Runtime.CompilerServices; | |
using System.Text; | |
using System.Threading.Tasks; | |
using Microsoft.AspNetCore.Builder; | |
using Microsoft.AspNetCore.Connections; | |
using Microsoft.AspNetCore.Hosting; | |
public class Startup | |
{ | |
private static readonly byte[] _helloWorldBytes = Encoding.UTF8.GetBytes("Hello, World!"); | |
public void Configure(IApplicationBuilder app) | |
{ | |
app.Run((httpContext) => | |
{ | |
var payload = _helloWorldBytes; | |
var response = httpContext.Response; | |
response.StatusCode = 200; | |
response.ContentType = "text/plain"; | |
response.ContentLength = payload.Length; | |
return response.BodyWriter.WriteAsync(payload).GetAsTask(); | |
}); | |
} | |
public static async Task Main(string[] args) | |
{ | |
var host = new WebHostBuilder() | |
.UseKestrel(options => | |
{ | |
options.Listen(IPAddress.Loopback, 5001); | |
}) | |
.UseContentRoot(Directory.GetCurrentDirectory()) | |
.UseStartup<Startup>() | |
.Build(); | |
await host.RunAsync(); | |
} | |
} | |
internal static class ValueTaskExtensions | |
{ | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static Task GetAsTask(this in ValueTask<FlushResult> valueTask) | |
{ | |
if (valueTask.IsCompletedSuccessfully) | |
{ | |
// Signal consumption to the IValueTaskSource | |
valueTask.GetAwaiter().GetResult(); | |
return Task.CompletedTask; | |
} | |
else | |
{ | |
return valueTask.AsTask(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment