Repository navigation
AES-GCM x86-64: AES-NI+AVX instances read the powers of H from a prepared key context - #1776
Merged
Merged
Conversation
…context (WIP) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
… AVX2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
…out a locked add Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The AES-NI+AVX interleaved loop (
Impl.Gcm.X86_64.StitchAvx8) computesH'²…H'⁸on every call. That is a serial chain of multiplications at the start of everysealof 256 bytes or more. With AAD it lands on the GHASH chain's critical path: at 384 B with a 13-byte AAD it costs about 7–9 ns.The AVX-512 backend avoids this with the
_preparedinstances. Once a key has madePOWERS_AFTERlong calls,Powers::getcomputes a 1024-byte key context with the powers (Spec.Gcm.PreparedPowersRepr), and the loops read them from it. This PR gives the AES-NI+AVX variant the same instances:StitchAvx8.encR:encwithsetupR, whosesetupGRloadsH'…H'⁸from the prepared context into the registers wheresetupGcomputes them (preg i, atprepOff (i + 1)). It is otherwise the same code:encForWith setupR. Decryption keepsdec.StitchName.encR/fullRfor.aesniAvx, and the variant'spieceR. The generic file then emits its_preparedinstances (init,seal,open,seal_gather,stream_encrypt/decrypt, the block functions), withStitchAvx8.stitchR_okasokR.vg_aes_gcm_init_preparedis the generic one, whose conversion uses AVX2. Its features now nameavxexplicitly, since the callees of this variant don't. The AES-NI+AVX backend needs only AVX, soBackend::preparedreturns the backend's_preparedinstances only where the CPU also has the key setup's features (here, AVX2). Sandy and Ivy Bridge keep computing the powers.src/aes_gcm.rs): the hard-coded_prepared_vaes_vpclmul_avx512calls become a table per backend (Prepared), chosen by an exhaustive match inBackend::prepared.Powersholds the key's table, looked up once inAesGcm::new. It also now counts the calls before the powers with a relaxed load and store rather than a lockedfetch_add: a count lost to a race only delays the powers. Without that, a key used for a single long message measured 7–15 ns slower than onmain. The AVX-512 backend gets the same saving.rust-cpu-featuresline and aCPU_FEATURESconfiguration foraes,avx,avx2,pclmulqdq,ssse3. That configuration now chooses differently fromaes,avx,pclmulqdq,ssse3(the prepared instances), so it is tested and benchmarked.This is part of #1228, following the rustls analysis in #991. A connection's key encrypts many records, which is the reused-key case.
Measurements
Intel (local Xeon @ 2.10 GHz, pinned to one core). The backend was AES-NI+AVX with AVX2 (
VG_CPU_FEATURES=avx,avx2,avx512f,avx512bw,avx512vl,bmi1,bmi2,adx,aes,pclmulqdq,ssse3), and aws-lc-rs was masked to the same features (OPENSSL_ia32cap='~0x0:~0x574220200000'). Each run used one key reused across messages and a 13-byte AAD, and took the best of 11. There were two alternating runs per side, #1750 (nowmain) → this PR. "+1" isAesGcm::encryptofn + 1bytes from two slices.* one outlier; the other run gave 331.
AES-NI+AVX now matches or beats aws-lc-rs at every size here. With a new key for each message (the case
POWERS_AFTERexists for), the cost per message is unchanged within noise: 257–269 ns → 258–267 ns at 384 B, and 370–382 → 371–380 at 1 KiB, encrypting.AMD (CI, AMD EPYC 7763): Benchmarks run against
main:aes,avx,avx2,pclmulqdq,ssse3aes,avx,pclmulqdq,ssse3aes-128-gcm-encrypt-reused-key/600aes-128-gcm-encrypt-reused-key/1350aes-128-gcm-encrypt-reused-key/4000aes-128-gcm-encrypt-two-pieces-reused-key/600aes-128-gcm-encrypt-two-pieces-reused-key/1350The fresh-key benchmarks (
encrypt,decrypt,out-of-place,two-pieces,stream), which never reach the prepared context, moved by 0–4% either way in both configurations. The configuration without AVX2 runs the same code as before. An earlier run on an EPYC 9V45, before the call-counting change, showed reused-key 3.6–9.6% faster and streaming up to 13% faster.Proofs
Assembly comes only from the Lean code. Spec, TCB and contracts are unchanged (
Spec.Gcm.PreparedPowersReprand the_preparedcontracts already exist).setup_okandencFor_okare now instances ofsetupWith_ok(any code leaving whatsetupGdoes:GPost) andencForWith_ok(any setup leavingReadyand the powers' law).setupGR_ok:setupGRlowers to SSE (lane0_setupGR), whose loads givereadW (ctx + prepOff n) = hInvF (Hⁿ)(prep_val, fromStitchZR.pair_values). Sincex · φ (hInvF (Hⁿ)) = φ(H)ⁿ(x_φ_hInvF), this isGPost. SoencR_okandstitchR_ok : StitchOkM CtxMode.prepared encR dec 1follow. These live inStitchAvx8/Ok.lean, the loop's one module with the field algebra, which keepsPoly.lean's importers at 40.encR_piece : Piece encR true trueanddecR_piece : Piece dec true(Ct.lean,encRmaterialized).prepared_boundariesandpowersnow run on every backend with prepared instances this CPU runs, not onlyVaesVpclmulAvx512.🤖 Generated with Claude Code
https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
Generated by Claude Code