Thanks to visit codestin.com
Credit goes to github.com

Skip to content

unbuffered: eliminate allocation in unbuffered bulk encrypt path#2698

Draft
hyrumcoop wants to merge 9 commits intorustls:mainfrom
hyrumcoop:hyrumcoop/zero-alloc-unbuffered-encrypt
Draft

unbuffered: eliminate allocation in unbuffered bulk encrypt path#2698
hyrumcoop wants to merge 9 commits intorustls:mainfrom
hyrumcoop:hyrumcoop/zero-alloc-unbuffered-encrypt

Conversation

@hyrumcoop
Copy link

Summary

This PR eliminates allocations in the unbuffered API bulk encrypt path without modifying the public API. This is achieved by adding an encrypt_into method on the MessageEncrypter trait that writes the encrypted record directly into a caller-provided buffer.

To be less intrusive, I introduced the structs PrefixedPayloadBorrowed and OutboundOpaqueMessageBorrowed alongside their owned counterparts. This temporarily duplicates some logic but leaves the original encrypt path untouched.

Benchmarks

Hardware: AMD Ryzen 7 5800XT
Command: BENCH_MULTIPLIER=32 taskset -c 4 ./target/release/rustls-bench --api=unbuffered bulk TLS13_AES_128_GCM_SHA256

Allocator Version Throughput (MB/s) Δ vs prev
system prev 5022.06
system zero-alloc 5253.56 +4.6%
jemalloc prev 5024.51
jemalloc zero-alloc 5243.84 +4.4%

Across both allocators, throughput improved by ~4-5%.

Motivation

Beyond the modest throughput increase, removing allocations in this path:

  • Reduces fragmentation caused by frequent allocation of variable-sized records, improving long-run consistency.
  • Reduces allocator contention under load.
  • Provides a foundation for future zero-copy optimizations.

Follow-up

  • Zero-copy encrypt: Once crypto providers expose an out-of-place API, we can eliminate the intermediate copy. In local experiments with aws-lc-rs, this yielded 15–35% higher throughput depending on machine, with even better multi-core scaling due to reduced DRAM traffic.

@hyrumcoop hyrumcoop force-pushed the hyrumcoop/zero-alloc-unbuffered-encrypt branch from da9ba29 to c831ad4 Compare October 3, 2025 11:37
@djc
Copy link
Member

djc commented Oct 3, 2025

Looks very interesting! The main branch is currently targeting 0.24 so I think we'd prefer to avoid duplication in favor of tweaking existing API.

@codecov
Copy link

codecov bot commented Oct 3, 2025

Codecov Report

❌ Patch coverage is 63.22314% with 89 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.52%. Comparing base (f96be2c) to head (c831ad4).

Files with missing lines Patch % Lines
rustls/src/msgs/message/outbound.rs 55.55% 36 Missing ⚠️
rustls/src/crypto/aws_lc_rs/tls13.rs 50.00% 22 Missing ⚠️
rustls/src/crypto/aws_lc_rs/tls12.rs 52.27% 21 Missing ⚠️
rustls/src/crypto/cipher.rs 0.00% 10 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2698      +/-   ##
==========================================
- Coverage   94.87%   94.52%   -0.35%     
==========================================
  Files          95       95              
  Lines       21794    22033     +239     
  Branches      614      616       +2     
==========================================
+ Hits        20676    20826     +150     
- Misses        992     1081      +89     
  Partials      126      126              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@hyrumcoop
Copy link
Author

Thanks for the feedback! That makes perfect sense.

I’ve been exploring a few ways to adjust the MessageEncrypter API so implementors can optionally take more control over their encryption data path without introducing unnecessary complexity or bloat to Rustls.

Once I’ve worked through the details, I’ll open a separate design discussion issue for the MessageEncrypter trait so we can agree on the right direction before committing to any specific changes.

@hyrumcoop hyrumcoop marked this pull request as draft October 5, 2025 19:30
@djc
Copy link
Member

djc commented Oct 6, 2025

Sounds great! FWIW, we already greatly appreciate the clear PR description and clean commit history.

@hyrumcoop
Copy link
Author

Hi @djc, thanks again for the feedback! I wanted to clarify a couple of things before continuing further:

  • I'm assuming there's a requirement to avoid breaking changes to the MessageEncrypter trait to maintain compatibility with third-party crypto providers. Is that correct?
  • You mentioned preferring API changes over duplication. Could you clarify which duplication you were referring to?
    • There is duplication in the aws-lc-rs/ring provider logic that I can easily resolve (we can concisely write encrypt in terms of encrypt_into)
    • There's also duplication in the sense that the encrypt_into method introduces a parallel data path (PrefixedPayloadBorrowed, OutboundOpaqueMessageBorrowed, RecordLayer::encrypt_outgoing_into) that mirrors the existing encrypt flow

@djc
Copy link
Member

djc commented Oct 9, 2025

  • I'm assuming there's a requirement to avoid breaking changes to the MessageEncrypter trait to maintain compatibility with third-party crypto providers. Is that correct?

No -- the main branch currently represents rustls 0.24, which will be allowed to break any and all public API as we're moving towards a rustls 1.0 release with a long-term stable API. So, unless you have requirements where you'd like to use these changes with rustls 0.23.x, we would prefer to optimize the API and avoid duplication for 0.24.x releases. (We can also publish 0.24.0-alpha releases if that helps you -- and these should be of high quality except for increased API churn -- although I'm not sure we want to do so for a whole bunch of downstream dependencies, like tokio-rustls and hyper-rustls.)

  • You mentioned preferring API changes over duplication. Could you clarify which duplication you were referring to?
    • There is duplication in the aws-lc-rs/ring provider logic that I can easily resolve (we can concisely write encrypt in terms of encrypt_into)
    • There's also duplication in the sense that the encrypt_into method introduces a parallel data path (PrefixedPayloadBorrowed, OutboundOpaqueMessageBorrowed, RecordLayer::encrypt_outgoing_into) that mirrors the existing encrypt flow

I mainly meant the latter, though in general I think we'd prefer to avoid duplication where that semantically makes sense.

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.

2 participants