Skip to content

Enhance simde/x86/gfni.h - #1429

Open
fo40225 wants to merge 13 commits into
simd-everywhere:masterfrom
fo40225:enhance_gfni
Open

Enhance simde/x86/gfni.h#1429
fo40225 wants to merge 13 commits into
simd-everywhere:masterfrom
fo40225:enhance_gfni

Conversation

@fo40225

@fo40225 fo40225 commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Summary

This change makes the GFNI emulation faster.
The changed files are simde/x86/gfni.h and test/x86/gfni.c.

New feature detection.

The macro SIMDE_X_GFNI_HAVE_AES detects hardware AES support.
The support comes from AES-NI on x86 or the crypto extension on ARMv8.
The macro SIMDE_X_GFNI_HAVE_SHUFFLE detects byte-shuffle support.

New AES path.

When the hardware has AES, the function simde_x_mm_gf2p8inverse_epi8 uses the AES S-box to compute the GF(2⁸) inverse.
New lookup tables undo the ShiftRows and affine steps of AES.
When the matrix is A_aes and the constant is 0x63, the affineinv operation becomes one SubBytes operation.

New tower-field path.

When the hardware has shuffle but no AES, the code computes the inverse and the multiply in the tower field GF((2⁴)²).
New lookup tables hold the log, exp, square, and basis-change values of GF(2⁴).
The multiply uses the Karatsuba method over GF(2⁴).

New constant-matrix path.

When the matrix is a compile-time constant, the code turns the matrix into two nibble lookup tables.
The affine transform then becomes two shuffles and one XOR at run time.
The new 256-entry table simde_x_gf2p8_mul_matrix_lut turns a multiply by a GF(2⁸) constant into an affine transform.
This path needs the two conditions SIMDE_CHECK_CONSTANT_ and SIMDE_X_GFNI_HAVE_SHUFFLE.
Without shuffle, the matrix multiply uses a slower movemask loop.
In that condition, the usual schoolbook multiply is faster.
Thus the code does not use the constant-multiply path without shuffle.

Removed old code.

This change removes the old lookup-table inverse implementations for SSE4.1, AVX2, and AVX512BW; the new methods are faster than a large table lookup.
CPUs that support AVX512 but not GFNI become much slower with 512-bit operations; thus the 512-bit functions now use the 256-bit fallback path.

New tests.

The file test/x86/gfni.c gets new test functions.

fo40225 added 11 commits August 16, 2026 12:00
Later commits use these features to compute GF(2^8) operations
without large lookup tables.
A GF(2) affine transform with a constant matrix is two 16-entry
table lookups, which the compiler computes at compile time. At run
time, the transform is two byte shuffles and one XOR.
This removes the run-time XOR with the broadcast affine constant.
…nsform

A multiply by a constant in GF(2^8) is a linear map over GF(2). A
precomputed table gives the affine matrix for each constant, and the
multiply then uses the constant-matrix nibble path. Without byte
shuffles, the matrix path is slower than the schoolbook multiply;
the code keeps the schoolbook multiply in that condition.
The AES S-box applies an affine transform after the GF(2^8)
inverse. The code removes that transform and keeps the inverse.
For a compile-time-constant matrix, the code folds the matrix and
the AES correction into two nibble tables. When the matrix is the
AES matrix and the constant is 0x63, the operation becomes one
SubBytes.
This path is for targets that have byte shuffles but no AES
instructions. The tower field turns the GF(2^8) inverse into
GF(2^4) operations, which 16-entry shuffle tables solve.
For a compile-time-constant matrix, the code folds the matrix into
the tables that map the tower-field inverse back to the standard
basis.
This path is for targets that have byte shuffles. The multiply uses
the Karatsuba method over GF(2^4) with log and exp tables.
The AES and tower-field paths are faster than the 256-byte lookup
table. The table remains only for targets that have no AES and no
byte shuffles. The wide inverse functions now use the narrower
fallbacks, which include the new paths.
The narrower fallbacks include the constant-operand and tower-field
paths. The removed wide-vector implementations did not.
@fo40225

fo40225 commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

@rosbif

Comment thread test/x86/gfni.c
SIMDE_TEST_GFNI_MUL_CONST(0x00); SIMDE_TEST_GFNI_MUL_CONST(0x01);
SIMDE_TEST_GFNI_MUL_CONST(0x02); SIMDE_TEST_GFNI_MUL_CONST(0x1b);
SIMDE_TEST_GFNI_MUL_CONST(0x53); SIMDE_TEST_GFNI_MUL_CONST(0x57);
SIMDE_TEST_GFNI_MUL_CONST(0x80); SIMDE_TEST_GFNI_MUL_CONST(0xff);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

warning C4309: 'static_cast': truncation of constant value

https://ci.appveyor.com/project/nemequ/simde/builds/54552506/job/hlmksf28wd24vg2y#L1168

@mr-c

mr-c commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

Thank you for this PR @fo40225

This change makes the GFNI emulation faster.

Do you have timing data to share? Can you explain a bit about your use case and motivation? We love to hear how people are using SIMDe, it is helpful to us!

@fo40225

fo40225 commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

SIMDe GFNI old vs new speedup (spd = old ÷ new, >1 = new is faster)

With GFNI hardware (rocketlake, alderlake, raptorlake, meteorlake, znver4):
both versions compile to a single native instruction all 1.00×, no difference.

Without GFNI hardware (emulated path):

Op Real x86 Zen4 (all march) Real x86 Nehalem ARM X925 (big core) ARM A725 (little core)
mul var 0.92~1.09× 1.15× ~1.00× ~1.00×
mul const 8.1~9.5× 6.7× ~1.00× 1.33×
affine var ~1.00× ~1.00× ~1.00× ~1.00×
affine const 9.7~10.4× 7.0× 4.75× 9.50×
affineinv var 1.1× (no AES) / 1.7~1.9× (AES) 1.57× 1.43× 0.66×
affineinv const 2.1~2.4× (no AES) / ~30× (AES) 2.48× 2.58× 1.49×

Summary: the new version wins big on constant-operand paths (folded at compile
time into nibble shuffles / AES fusion); variable paths are equal or slightly faster.
The only regression is affineinv var on the ARM A725 little core (0.66×, where a LUT
beats the tower-field approach on narrow cores); the tower implementation was kept
anyway no special case added for a single little core.

@fo40225

fo40225 commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

I am trying to write a faster zlib compression and decompression library based on GFNI instructions (although it haven't succeeded yet).

Recent PRs was just a quick fix for issues I ran into while using simde.

Add AVX2 versions of the tower-field multiply, the inverse, and the
fused affine inverse with a constant matrix. One broadcast moves each
16-byte table to the two ymm lanes. The wide multiply, inverse, and
affine-inverse operations use these paths when AVX2 is available.
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