Skip to content

Speedup f4 - #390

Open
ederc wants to merge 3 commits into
algebraic-solving:masterfrom
ederc:speedup-f4
Open

ederc wants to merge 3 commits into
algebraic-solving:masterfrom
ederc:speedup-f4

Conversation

@ederc

@ederc ederc commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

This PR speeds up F4 over finite fields and the application of the tracer in the multi-modular computations over QQ. The computed bases and all outputs are unchanged.

Changes

Speed up F4 with optimizations taken from fast

  • Symbolic preprocessing picks the shortest reducer and searches for reducers in parallel. The multiplied rows are built with parallel hash table lookups, then the missing monomials are inserted serially in row order. The hash table is therefore identical to that of a serial run.
  • Linear algebra:
    • Barrett reduction for the modular arithmetic.
    • Known pivot rows are stored in one block, in the order of their lead terms.
    • Very sparse rows are reduced via a heap over their nonzero columns.
    • Rows that are dense on the new columns are echelonized with a dense AVX2 kernel.
  • The Gebauer–Möller pair update runs in bulk parallel steps, with the same result as the element-by-element update.

Cache traced matrices when applying the tracer over QQ

When the learned trace is applied to further primes, each prime rebuilt the same matrix structure: it hashed the multiplied rows, converted monomials to columns and sorted them. Only the coefficients differ between primes.

  • The first application now stores each round's matrix in column form.
  • Later primes copy that matrix. They do so only while the supports of the basis elements used in the round agree with the recorded ones; after the first mismatch the cache is turned off for that prime.
  • The trace may be applied to several primes in parallel, so entries are published with compare-and-swap.

Compress the matrices stored in the trace cache

  • Rows are stored as varints (variable-length-integers), with column indices as zigzag-encoded differences. Most of them fit in one byte.
  • This cuts the extra memory used by the cache by about 70%, at a cost of about 1–2% in running time.

Benchmarks

Times are in seconds.

Finite fields, F4 only

system 1 thread before 1 thread after 8 threads before 8 threads after
kat12-31 15.8 14.3 5.6 2.8
kat13-31 141.1 130.4 35.7 28.2
eco13-31 21.9 17.4 8.8 4.3
eco14-31 183.9 145.9 54.6 35.6
mayr42-31 34.6 21.5 30.9 26.1
bayes148-31 32.3 10.3 30.3 22.1
gametwo7-31 14.9 14.6 5.4 4.4
kat14-31 1163.9 1155.2 314.6 267.5

QQ, -g 2

The "before" times are from master. The "after" times and the memory column are measured with the compressed cache.

system 1 thread before 1 thread after 8 threads before 8 threads after peak memory before → after
kat11-qq 6.3 4.6 3.8 1.7 63 → 69 MB
eco12-qq 7.9 5.5 4.8 2.2 60 → 74 MB
kat12-qq 57.3 41.9 27.7 ~12 262 → 291 MB
eco13-qq 57.2 38.8 29.2 11.2 198 → 256 MB
noon9-qq 13.5 11.7 8.4 6.4 265 → 274 MB

Systems that need only a few primes are dominated by learning and stay roughly unchanged (bayes148-qq, mayr42-qq).

Testing

  • make check: 71/71 pass.
  • 600 runs compared against master over all inputs in input_files with several option sets: no differences.
  • ASan: 134 runs, no errors.
  • QQ inputs compared against master (default solving, -g 2 and -P 1, with 1–3 threads): no differences.

Parts of this PR were written with the assistance of generative AI (Claude Code). The commits carry the corresponding co-author lines.

ederc and others added 3 commits November 5, 2026 08:55
Port several optimizations of the fast F4 implementation to neogb.

Symbolic preprocessing and pair selection: reducers are chosen as the
shortest divisor (the newest one among equally long ones), the columns
are processed in waves whose reducer searches run in parallel, and the
multiplied rows are built by parallel hash table lookups followed by a
serial insertion of the missing monomials in row order, so the hash
table is the same as for a serial run.

Linear algebra: modular reductions use Barrett reduction, the known
pivot rows are copied into one block in the order of their lead terms,
very sparse rows are reduced via a heap of their nonzero columns
instead of a dense row over all columns, and if the rows to be reduced
are dense on the new columns their remainders are echelonized as dense
rows with an AVX2 kernel. The unused statistics updates in the kernels
are removed.

Pair update: the new elements of a round are handled in bulk parallel
steps with the same result as the element by element Gebauer-Moeller
update, and only the lcms of the remaining pairs are inserted into the
hash table.

The computed bases are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the multi-modular computation over the rationals applies the
learned trace for further primes, every prime rebuilt the same matrix
structure: hashing the multiplied rows, converting monomials to columns
and sorting them. Only the coefficients differ between primes.

The first application now stores each round's matrix in column form
together with the exponent vectors of its new columns. Later primes
copy the cached rows if the supports of the basis elements used in that
round agree with the ones recorded for the trace; after the first
mismatch the cache is disabled for the rest of that prime. Entries and
references are published with compare-and-swap, since the solver
applies the trace for several primes in parallel. On a cache hit the
reducer rows are handed to the linear algebra as one block.

This speeds up the tracer phase by 1.2-1.4x on one thread and up to
2.3x on eight threads for application-dominated systems, at the cost of
additional memory for the cached matrices.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The matrices cached when applying the tracer over the rationals stored
every column index as a full word, which increased the peak memory
usage considerably, e.g. from 198 MB to 426 MB for eco13.

The rows are now stored as varints: the header entries directly and
the column indices as zigzag encoded differences to the previous column
index of the row, most of which fit into a single byte. The start of
each row is stored as well, so that the rows can be decompressed in
parallel when the cached matrix is reused.

This reduces the additional memory usage of the cache by about 70%
(eco13 now needs 256 MB) while the running time increases by only
about 1-2%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant