Skip to content

Instantly share code, notes, and snippets.

@benaadams
Last active January 27, 2022 23:42
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benaadams/cd2969e13c582ab4070489b5c585024e to your computer and use it in GitHub Desktop.
Save benaadams/cd2969e13c582ab4070489b5c585024e to your computer and use it in GitHub Desktop.
Testing ValueTask pooling
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
}
}
}
// 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