Use windowed shift-and-mask digit extraction in GetDigitAtIndexForBase - #1243
Merged
Conversation
yspolyakov
approved these changes
Aug 5, 2026
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.
The scalar primitive recomputed a floating-point log2 on every call and
assembled each digit one bit at a time; the NativeVector overload then
invoked it per coefficient through the shared library, so nothing hoisted
or vectorized. Extract the digit as a single shift-and-mask window
instead, with the shift and mask hoisted across the vector so the loop
auto-vectorizes. Digits are unchanged everywhere the old code was
defined; windows past the word width now return 0 instead of reading
past it (previously undefined for partial top windows, e.g. a 60-bit
modulus with 13-bit digits).
BV key switching consumes this on every relinearization and rotation.
On Ice Lake (Xeon 8360Y) at 1 thread across {clang-18, gcc-14} x
{native-opt on, off}: the primitive is 46-150x faster,
NativePoly::BaseDecompose 21-150x, DCRTPoly::CRTDecompose 1.45-2.7x,
and BGV BV-mode EvalMult+relin / EvalRotate 1.22-1.90x, with no
regressions; the gains hold at 36 threads under clang (1.19-1.83x).
Verified digit-for-digit against the previous implementation and by
exact reconstruction; all core and pke unit tests pass.