Skip to content

Commit

Permalink
Use a custom interpolation handler for paths (#266)
Browse files Browse the repository at this point in the history
Motivation
----------
Improved performance and reduced heap allocations building URLs for requests. While parts of this could be backported to downlevel frameworks, it's generally not worth the effort, so it only applies to .NET 6 and newer.

Modifications
-------------
- Add `TrySerialize` to `LiteralSerializer` that writes to a `Span<char>`
- Create a custom interpolated string handler for building URIs
- Add an overload to `PathSegmentSerializer` that accepts a `PathSegmentInterpolatedStringHandler`
- Use the new overload when building the `BuildUri` method when targeting .NET 6 and later
- Add a benchmark project

Breaking Changes
--------------------
- `PathParser.ToInterpolatedStringExpression` and `PathSegment.ToInterpolatedStringContentSyntax` now take an interpreter that returns an `InterpolationSyntax` instead of an `ExpressionSyntax`

Results
-------
The benchmark below is for a relatively simple URI with two path segments, one a long and the other a GUID. The Build benchmark uses the overload that stackallocs an initialBuffer.

BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.2033) Unknown processor
.NET SDK 8.0.403
  [Host]     : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX2

| Method    | Mean     | Error    | StdDev   | Ratio | Gen0   | Allocated | Alloc Ratio |
|---------- |---------:|---------:|---------:|------:|-------:|----------:|------------:|
| Serialize | 28.92 ns | 0.176 ns | 0.156 ns |  1.00 | 0.0216 |     272 B |        1.00 |
| Build     | 24.10 ns | 0.153 ns | 0.136 ns |  0.83 | 0.0108 |     136 B |        0.50 |
  • Loading branch information
brantburnett authored Oct 21, 2024
1 parent cb04589 commit 81b3c2f
Show file tree
Hide file tree
Showing 13 changed files with 1,640 additions and 36 deletions.
25 changes: 25 additions & 0 deletions src/main/Yardarm.Benchmarks/PathSerializerInterpolation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using RootNamespace.Serialization;

namespace Yardarm.Benchmarks;

[MemoryDiagnoser]
public class PathSerializerInterpolation
{
long BusinessLocationId { get; set; } = 123456;
Guid CustomerId { get; set; } = Guid.NewGuid();

[Benchmark(Baseline = true)]
public string Serialize()
{
// We don't use stackalloc for an initial buffer here because this PathSegmentSerializer.Serialize overload returns intermediate strings,
// so this is implemented as a call to string.Concat which is faster than using DefaultInterpolatedStringHandler.
return $"org/{PathSegmentSerializer.Serialize("id", BusinessLocationId, PathSegmentStyle.Simple, "int64")}/customers/{PathSegmentSerializer.Serialize("customerId", CustomerId, PathSegmentStyle.Simple, "uuid")}";
}

[Benchmark]
public string Build()
{
Span<char> initialBuffer = stackalloc char[256];
return PathSegmentSerializer.Build(initialBuffer, $"org/{BusinessLocationId:int64}/customers/{CustomerId:uuid}");
}
}
3 changes: 3 additions & 0 deletions src/main/Yardarm.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using BenchmarkDotNet.Running;

BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
24 changes: 24 additions & 0 deletions src/main/Yardarm.Benchmarks/Yardarm.Benchmarks.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" VersionOverride="0.14.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Yardarm.Client\Yardarm.Client.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="BenchmarkDotNet" />
<Using Include="BenchmarkDotNet.Attributes" />
</ItemGroup>

</Project>
Loading

0 comments on commit 81b3c2f

Please sign in to comment.