Skip to content

SDK Performance Boost - #5840

Open
lisandroct wants to merge 9 commits into
masterfrom
lisandro/sdk-performance
Open

SDK Performance Boost#5840
lisandroct wants to merge 9 commits into
masterfrom
lisandro/sdk-performance

Conversation

@lisandroct

@lisandroct lisandroct commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Description of Changes

It cleans up a couple of things to achieve better performance in the C# SDK:

  1. It replaces the usage of LINQ (with direct for loops, for example);
  2. Reduces the total amount of allocations by pooling arrays and avoiding some calls to .ToArray();
  3. Reduces the initial size of websockets and resizes when necessary (this also reduces a lot of allocations).

API and ABI breaking changes

No changes. It's all internal.

Expected complexity level and risk

  1. We need to make sure BSATN decoding stays consistent for all paths (I added a unit test for this) and that websockets behavior stays the same as before (just less memory hungry).

Testing

  • Run benchmarks
  • Tests all still pass

Benchmarks

I added two benchmarks:

xUnit Benchmarks:

Benchmark Rows / Variant Master mean PR mean Time delta Master alloc PR alloc Alloc delta
DecompressDecodeMessage transaction_update 1, uncompressed 0.49 us 0.41 us -16.3% 1,544 B 1,200 B -344 B
DecompressDecodeMessage transaction_update 100, uncompressed 2.87 us 2.74 us -4.5% 2,128 B 1,592 B -536 B
DecompressDecodeMessage transaction_update 10,000, uncompressed 89.01 us 85.28 us -4.2% 81,328 B 41,192 B -40,136 B
DecompressDecodeMessage transaction_update 1, brotli 2.22 us 1.46 us -34.2% 67,232 B 1,672 B -65,560 B
DecompressDecodeMessage transaction_update 100, brotli 5.36 us 4.61 us -14.0% 67,816 B 2,256 B -65,560 B
DecompressDecodeMessage transaction_update 10,000, brotli 197.16 us 195.17 us -1.0% 147,017 B 81,457 B -65,560 B
Reducer/procedure payload 1 0.23 us 0.25 us +8.7% 936 B 904 B -32 B
Reducer/procedure payload 100 4.55 us 4.61 us +1.3% 5,840 B 5,408 B -432 B
Reducer/procedure payload 10,000 233.89 us 300.59 us +28.5% 571,663 B 531,630 B -40,033 B
DecompressDecodeMessage initial_connection uncompressed 0.13 us 0.09 us -30.8% 728 B 384 B -344 B
DecompressDecodeMessage initial_connection brotli 1.46 us 0.45 us -69.2% 66,416 B 856 B -65,560 B
Brotli inflate transaction_update 1 1.13 us 1.13 us 0.0% 936 B 936 B 0 B
Brotli inflate transaction_update 100 1.98 us 1.96 us -1.0% 1,128 B 1,128 B 0 B
Brotli inflate transaction_update 10,000 109.13 us 108.40 us -0.7% 40,728 B 40,710 B -18 B
MultiDictionary.Apply 1 0.15 us 0.15 us 0.0% 872 B 872 B 0 B
MultiDictionary.Apply 100 8.42 us 8.41 us -0.1% 43,544 B 43,544 B 0 B
MultiDictionary.Apply 10,000 1,202.86 us 1,174.74 us -2.3% 4,234,208 B 4,234,253 B +45 B
ParseRowList 1 0.05 us 0.05 us 0.0% 224 B 224 B 0 B
ParseRowList 100 3.22 us 3.28 us +1.9% 2,600 B 2,600 B 0 B
ParseRowList 10,000 244.97 us 276.34 us +12.8% 240,200 B 240,200 B 0 B

BenchmarkDotNet:
I started with the xUnit benchmark from above and I liked the look of the allocation deltas a lot. But the reducer/procedure time delta didn't look so nice, so I also added this BenchmarkDotNet project that should be a little more accurate and the equivalent benchmark (the first three) now look a lot more flat.

Benchmark Rows Master mean PR mean Time delta Master alloc PR alloc Alloc delta
XUnitReducerProcedureShape 1 103.57 ns 112.14 ns +8.3% 936 B 904 B -32 B
XUnitReducerProcedureShape 100 1.394 us 1.422 us +2.0% 5,840 B 5,408 B -432 B
XUnitReducerProcedureShape 10,000 130.733 us 131.045 us +0.2% 571,663 B 531,631 B -40,032 B
SerializeAndDecodeFromByteArray 1 85.93 ns 82.72 ns -3.7% 840 B 840 B 0 B
SerializeAndDecodeFromByteArray 100 1.359 us 1.376 us +1.3% 4,944 B 4,944 B 0 B
SerializeAndDecodeFromByteArray 10,000 124.907 us 133.552 us +6.9% 491,566 B 491,566 B 0 B
SerializeToListOnly 1 56.57 ns 57.33 ns +1.3% 536 B 536 B 0 B
SerializeToListOnly 100 830.12 ns 845.00 ns +1.8% 1,872 B 1,872 B 0 B
SerializeToListOnly 10,000 75.081 us 77.035 us +2.6% 211,288 B 211,288 B 0 B
DecodeFromListOnly 1 45.78 ns 53.04 ns +15.9% 400 B 368 B -32 B
DecodeFromListOnly 100 582.62 ns 584.81 ns +0.4% 3,968 B 3,536 B -432 B
DecodeFromListOnly 10,000 56.498 us 52.541 us -7.0% 360,373 B 320,340 B -40,033 B
DecodeFromByteArrayOnly 1 41.55 ns 41.80 ns +0.6% 368 B 368 B 0 B
DecodeFromByteArrayOnly 100 565.05 ns 566.06 ns +0.2% 3,536 B 3,536 B 0 B
DecodeFromByteArrayOnly 10,000 50.594 us 55.436 us +9.6% 320,340 B 320,340 B 0 B

@lisandroct
lisandroct marked this pull request as draft August 28, 2026 18:29
@lisandroct
lisandroct force-pushed the lisandro/sdk-performance branch from 7b86fbb to 4739cb5 Compare August 31, 2026 14:29
@lisandroct
lisandroct marked this pull request as ready for review September 1, 2026 19:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant