Skip to content

Conversation

@DeagleGross
Copy link
Member

@DeagleGross DeagleGross commented Dec 12, 2025

#44758 introduced new DataProtection API, and in this PR I am changing Antiforgery implementation to use a high-performance DataProtection API. Moreover, going through the implementation, I've refactored to improve performance here and there. The results are listed below.

The main idea of the improvement is to use the DataProtection API, which does not make us allocate any buffer. This is a hot-path in Antiforgery, because token serialization and deserialization may happen multiple times for different tokens per single request.

Before, we had to allocate a buffer, fill it in with the token data, pass it into the dataProtector and later we were using streams API, which also allocate and may be doing extra job instead of writing to the destination buffer. There are less of extra types used today, which should give some benefit to the usage.

I have tested the changes via crank, but unfortunately I dont see a heavy improvement in RPS speed (around 4% RPS improvement only). I suspect those improvements mostly focus on allocation reduction, meaning in the long run it will give more RPS.

IAntiforgeryTokenSerializer

branch Method Mean Error StdDev Op/s Gen 0 Gen 1 Gen 2 Allocated
main Serialize 2.221 us 0.0245 us 0.0229 us 450,239.5 0.0076 - - 872 B
main Deserialize 2.436 us 0.0463 us 0.1036 us 410,492.5 0.0076 - - 632 B
change Serialize 1.932 us 0.0110 us 0.0098 us 517,688.9 0.0038 - - 544 B
change Deserialize 1.927 us 0.0107 us 0.0100 us 519,058.2 0.0038 - - 344 B

IAntiforgeryTokenGenerator

branch Method Mean Error StdDev Op/s Gen 0 Gen 1 Gen 2 Allocated
main GenerateRequestToken_Anonymous 11.0555 ns 0.1203 ns 0.1066 ns 90,452,434.9 0.0007 - - 56 B
main GenerateRequestToken_Authenticated 401.2545 ns 7.1693 ns 6.3554 ns 2,492,184.2 0.0076 - - 592 B
main TryValidateTokenSet_Anonymous 6.7227 ns 0.0357 ns 0.0316 ns 148,750,552.9 - - - -
main TryValidateTokenSet_Authenticated 508.1742 ns 4.4728 ns 3.7350 ns 1,967,829.1 0.0095 - - 760 B
main TryValidateTokenSet_ClaimsBased 308.4674 ns 3.3256 ns 3.1108 ns 3,241,833.1 0.0038 - - 312 B
change GenerateRequestToken_Anonymous 11.190 ns 0.2428 ns 0.6046 ns 89,364,681.9 0.0007 - - 56 B
change GenerateRequestToken_Authenticated 338.056 ns 6.7313 ns 14.9161 ns 2,958,092.2 0.0052 - - 424 B
change TryValidateTokenSet_Anonymous 7.966 ns 0.1616 ns 0.2915 ns 125,531,038.3 - - - -
change TryValidateTokenSet_Authenticated 13.386 ns 0.2476 ns 0.3550 ns 74,707,554.5 - - - -
change TryValidateTokenSet_ClaimsBased 220.111 ns 4.2723 ns 5.7034 ns 4,543,156.3 0.0014 - - 120 B

IAntiforgery

branch Method Mean Error StdDev Op/s Gen 0 Gen 1 Gen 2 Allocated
main GetAndStoreTokens 59.56 us 2.482 us 7.082 us 16,789.6 - - - 5 KB
main ValidateRequestAsync 50.60 us 2.150 us 6.167 us 19,764.1 - - - 4 KB
change GetAndStoreTokens 49.62 us 1.386 us 3.954 us 20,153.9 - - - 3 KB
change ValidateRequestAsync 43.67 us 1.541 us 4.471 us 22,900.6 - - - 3 KB

Relates to #50065

@DeagleGross DeagleGross self-assigned this Dec 12, 2025
@DeagleGross DeagleGross added area-perf Performance infrastructure issues feature-antiforgery labels Dec 12, 2025
@DeagleGross DeagleGross marked this pull request as ready for review December 12, 2025 16:10
Copilot AI review requested due to automatic review settings December 12, 2025 16:10
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces significant performance improvements to the Antiforgery system by leveraging new span-based DataProtection APIs introduced in #44758. The changes eliminate buffer allocations and stream-based serialization in favor of direct span operations, resulting in substantial improvements across all measured scenarios: 13-15% reduction in allocations and 15-20% improvement in throughput.

Key Changes:

  • Span-based DataProtection: Replaces byte array allocations with ISpanDataProtector for zero-copy data protection operations
  • 7-bit encoding utilities: Adds shared utilities for efficient length-prefixed string serialization using span-based APIs
  • Removed object pooling: Eliminates AntiforgerySerializationContext pooling infrastructure now that allocations are minimal

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/Shared/Encoding/Int7BitEncodingUtils.cs Adds span-based methods for reading/writing 7-bit encoded integers and strings
src/Shared/test/Shared.Tests/Encoding/Int7BitEncodingUtilsTests.cs Comprehensive test coverage for new encoding utilities
src/Shared/WebEncoders/WebEncoders.cs Adds Base64Url decoding directly to span for .NET 9+
src/Antiforgery/src/Internal/DefaultClaimUidExtractor.cs Converts to span-based claim UID extraction using stack allocation and direct SHA256 hashing
src/Antiforgery/src/Internal/DefaultAntiforgeryTokenSerializer.cs Converts to span-based serialization using ISpanDataProtector API
src/Antiforgery/src/Internal/DefaultAntiforgeryTokenGenerator.cs Updates to use span-based claim UID comparison
src/Antiforgery/src/Internal/AntiforgerySerializationContext.cs Removed - no longer needed with span-based approach
src/Antiforgery/src/Internal/AntiforgerySerializationContextPooledObjectPolicy.cs Removed - pooling infrastructure no longer needed
src/Antiforgery/src/AntiforgeryServiceCollectionExtensions.cs Removes object pool registration
src/Antiforgery/test/DefaultClaimUidExtractorTest.cs Updates tests for new span-based API signature
src/Antiforgery/test/DefaultAntiforgeryTokenSerializerTest.cs Adds TestSpanDataProtector mock implementation
src/Antiforgery/test/DefaultAntiforgeryTokenGeneratorTest.cs Adds DummyClaimUidExtractor helper for testing
src/Antiforgery/benchmarks/Microsoft.AspNetCore.Antiforgery.Benchmarks/* New benchmark project with comprehensive performance tests

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
using System.Text;
using System;
using System.Text;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dont get the suggestion - System is unnecessary here

@dotnet-policy-service dotnet-policy-service bot added the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Dec 24, 2025
@DeagleGross
Copy link
Member Author

Latest results. Speed overall became even a bit faster because of tokenvalidation/generation changes.

Method Mean Error StdDev Op/s Gen 0 Gen 1 Gen 2 Allocated
Serialize 1.917 us 0.0205 us 0.0171 us 521,767.1 0.0038 - - 544 B
Deserialize 1.971 us 0.0318 us 0.0282 us 507,477.4 0.0038 - - 344 B
Method Mean Error StdDev Op/s Gen 0 Gen 1 Gen 2 Allocated
GenerateRequestToken_Anonymous 10.305 ns 0.2220 ns 0.3829 ns 97,041,341.0 0.0007 - - 56 B
GenerateRequestToken_Authenticated 290.905 ns 4.5755 ns 4.0560 ns 3,437,544.2 0.0052 - - 424 B
TryValidateTokenSet_Anonymous 7.251 ns 0.1283 ns 0.1426 ns 137,916,412.4 - - - -
TryValidateTokenSet_Authenticated 15.011 ns 0.2482 ns 0.2322 ns 66,618,628.2 - - - -
TryValidateTokenSet_ClaimsBased 200.315 ns 2.1224 ns 1.8815 ns 4,992,129.3 0.0021 - - 120 B
Method Mean Error StdDev Median Op/s Gen 0 Gen 1 Gen 2 Allocated
GetAndStoreTokens 44.70 us 1.542 us 4.547 us 45.20 us 22,372.9 - - - 3 KB
ValidateRequestAsync 40.65 us 1.565 us 4.566 us 42.25 us 24,597.8 - - - 3 KB

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-perf Performance infrastructure issues feature-antiforgery pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants