Skip to content

kernel: conversion cache optimization experiment - #22401

Draft
olympichek wants to merge 11 commits into
rocq-prover:masterfrom
olympichek:conversion-cache-experimental
Draft

kernel: conversion cache optimization experiment#22401
olympichek wants to merge 11 commits into
rocq-prover:masterfrom
olympichek:conversion-cache-experimental

Conversation

@olympichek

Copy link
Copy Markdown
Contributor

A draft PR with some further optimizations on top of the conversion cache implementation proposed in PRs #22309, #22319, #22396

olympichek and others added 11 commits August 25, 2026 11:46
A telescope-heavy kernel check compares the same pair of closures over and
over: beta-substitution shares payload cells across every occurrence of a
variable, so the same two cells meet again at each copy of the term they
were substituted into. The reduction machine has no memory of this and
re-derives each convertibility subproblem from scratch.

Give each fconstr cell a stable id (a new lazily-assigned mutable field,
preserved by the in-place [update] the machine uses for reduction and
reset on every copy), and add a per-session table in `ccnv` mapping a pair
of cell ids plus lift pair and conversion problem to the outcome
(convertible or not). On a hit the outcome is replayed directly; both
successes and failures are cached.

Enabled only for checked conversion, where the result is a deterministic
function of the fixed environment and no universe constraints are
accumulated to replay; inference-mode conversion (which backtracks and
discards constraints) never consults the cache. On by default;
ROCQ_CONV_CACHE=0 disables it, ROCQ_CONV_CACHE_MAX bounds the table.

Cuts the heaviest checked conversion we measured by ~4x.
Entries in the conversion cache were boxed tuples holding lift values,
scanned with `eq_lift` inside per-key assoc lists. Intern lifts to small
ids (`hash_lift` added to `Esubst` for the interning table) so that each
entry packs into two machine integers: the cell-id pair as the key
(`(fid1 lsl 31) lor fid2`) and the lift ids, problem kind and result as
the payload. The container is unchanged (a `Hashtbl`, several bindings
per packed key). Problems whose lifts or ids exceed the packing range
fall back to uncached conversion.

Behaviour is otherwise unchanged: identical probe/hit/insert counts.
The packed entries still live in a `Hashtbl`, whose internal bucket
cells are heap blocks: on a heavy check that inserts ~60M entries within
a single session the major GC traces every one of them, and that tracing
becomes the dominant cost.

Since an entry is already two machine integers, store the cache in two
flat int arrays with open addressing (linear probing, integer mix hash,
doubling growth). No per-entry allocation and nothing for the GC to
trace.

Behaviour is unchanged (identical probe/hit/insert counts on the
benchmark, digit for digit). On the heaviest check this is a further ~2.6x
in time and drops peak memory by about a third.
The conversion cache memoizes verdicts by cell identity, which is blind
to cells rebuilt over the same closure: the same (body, substitution)
question asked through fresh `FCLOS` wrappers re-runs the whole recursive
comparison. Add a second cache level, probed when the cell-id lookup
misses on an `FCLOS`/`FCLOS` pair, keyed on the closure content: body
`constr` by pointer and substitution by the spine of cells it contains,
compared through the stable cell fids (`CClosure.subs_content_fid`,
`subs_content_equal`, `Esubst.Internal.repr`), which survive in-place
cell updates. Verdicts found at this level are fed back into the cell-id
level. Session-scoped, same soundness domain as the cell-id cache.

On by default; `ROCQ_CLOS_MEMO=0` disables, `=stats` observes without
acting (diagnostics, includes a body-pair upper-bound counter),
`ROCQ_CLOS_MEMO_STATS=1` prints counters at exit.

SSGpd.v: 89.3 s -> 67.5 s (-24.5 %) at 5.36 GB -> 1.41 GB maxRSS
(-74 %), byte-identical `.vo`. In observe mode 66 % of cell-id misses
are exact (node, subst) repeats. On workloads whose repeats are nested
(nutele) the effect is a mild win (-7 %/-14 %).
The (node, subst) probe cost was dominated by key bookkeeping rather
than the table: `subs_hash`/`subs_equal` went through
`Esubst.Internal.repr`, materializing both spines as lists on every
probe, and the find-then-add pattern ran the two deep
`Hashtbl.hash_param` body traversals twice on the miss path.

Add `Esubst.Internal.fold` (allocation-free fold over the entries in
`repr` order, also returning the total shift) and
`Esubst.Internal.equal` (structural fast path, `repr` fallback for
substitutions built through different construction paths), and
precompute the key hash in a `ck_hash` field: bodies are hashed once
per probe, the table hash is a field read, and equality prefilters on
it. Cache decisions are unchanged: probe/hit/insert counters and the
produced `.vo` are byte-identical to the previous code on SSGpd and
the Bonak abstract-layer-module files; `Esubst.Internal.fold/equal`
are validated against `repr` on 200k random substitutions.

Paired runs: SSGpd 68.0 s -> 58.2 s (-14.5 %; maxRSS +6 % from the
stored hash word). The default-on probe-tax regressions invert into
wins: νSet 2.39 s -> 1.96 s and νDgnSet 8.38 s -> 7.11 s, both now
faster than the memo-less baseline (2.25 s / 7.64 s); νGpd
94.2 s -> 82.8 s at -8.5 % maxRSS.
We quickly assess whether we may have a fconstr hit by recording the
highest id present in the table.

(cherry picked from commit 16d8830)
Some conversion problems involve fconstr freshly generated by mk_clos.
These objects are not reference elsewhere, so it makes no sense to
either check for their presence in the cache or even try to cache their
result. We detect this situation by passing an additional cache-disabling
flag to the conversion function, and let mk_clos set a fresh id when the
fconstr is actually not fresh, i.e. when it comes from an expansion of
a rel in the environment.

(cherry picked from commit ab80f4f)
We reserve for the identity lift the specific uid 0 and do not try
to query the hash table. Most lifts are identity.

(cherry picked from commit bd10544)
Inlining `clos_rel` into `mk_clos` collapsed its two `HigherOrder`
branches into the "Uncaught pattern variable" anomaly, but
`HigherOrder (0, mt)` is a legitimate case: it is a rewrite-rule hole
applied to zero arguments, and `clos_rel` mapped it to `Regular mt`.
Since the RHS substitution of every matched rewrite rule is built out
of `HigherOrder` entries before being handed to `mk_clos`, any rule
with a bare hole in its RHS raised the anomaly. `klt` handles the same
case explicitly.

Unlike the other cells `mk_clos` builds, the value behind a hole is
shared across all its occurrences in the RHS, so it is worth interning
for the conversion cache; hence the `get_fid`.

Restore `test-suite/success/rewrule.v` and
`test-suite/bugs/bug_20728.v`, which were removed because they tripped
the anomaly. `test-suite/success/UnfoldDepHeuristic.v` stays as is: its
negative control is defeated by the conversion cache itself, which this
commit does not change.

(cherry picked from commit 843ef3ccd4151589a2dc4c3fcab304fbc868554d)
`~cache` is a fid-assignment policy, not a caching policy: it decides
whether to intern a cell nobody else references. That is a level-1
concern only. Level 1 is keyed on cell identity, so a freshly built cell
gets a brand new id, the probe can never hit (`max_uid` rejects it) and
the insert is pure bookkeeping. Level 2 is keyed on `(node, subst)` and
needs no id for the compared cells themselves -- only for the
substitution entries, which are shared by construction -- so it applies
to freshly built closures as well.

Those closures are exactly what the `~cache:false` sites produce:
`convert_under_context` compares `mk_clos`-fresh branch bodies and
return clauses, and fix/cofix bodies, prod codomains and `Zlcase`
parameters are all `FCLOS`. Gating both levels on `~cache` therefore
starved level 2 of nearly all its input.

Gate level 1 on the old condition, gate level 2 on both sides being
`FCLOS`, and skip the probes entirely only when neither applies. An
out-of-range fid now falls back to level 2 alone instead of dropping
both.
@coqbot-app coqbot-app Bot added the needs: full CI The latest GitLab pipeline that ran was a light CI. Say "@coqbot run full ci" to get a full CI. label Aug 25, 2026
@olympichek

Copy link
Copy Markdown
Contributor Author

@herbelin @ppedrot
Can someone please run a bench here?

@ppedrot

ppedrot commented Aug 25, 2026

Copy link
Copy Markdown
Member

@coqbot run full ci

@coqbot-app coqbot-app Bot removed the needs: full CI The latest GitLab pipeline that ran was a light CI. Say "@coqbot run full ci" to get a full CI. label Aug 25, 2026
@ppedrot

ppedrot commented Aug 25, 2026

Copy link
Copy Markdown
Member

@coqbot bench

@coqbot-app

coqbot-app Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🏁 Bench results:

┌─────────────────────────────────────┬──────────────────────────┬────────────────────────────────────────┬──────────────────────────┐
│                                     │      user time [s]       │            CPU instructions            │  max resident mem [KB]   │
│                                     │                          │                                        │                          │
│            package_name             │   NEW      OLD    PDIFF  │      NEW             OLD        PDIFF  │   NEW      OLD    PDIFF  │
├─────────────────────────────────────┼──────────────────────────┼────────────────────────────────────────┼──────────────────────────┤
│                         coq-unimath │ 1639.10  1937.42  -15.40 │ 12727953733556  15984192370171  -20.37 │ 1710600  1799416   -4.94 │
│                        coq-bedrock2 │  296.22   333.55  -11.19 │  2317035870559   2684205948697  -13.68 │  855704   874004   -2.09 │
│              rocq-mathcomp-analysis │ 1229.81  1382.54  -11.05 │  8701669743972  10383596259511  -16.20 │ 2199996  2196116    0.18 │
│                            coq-core │    2.70     2.79   -3.23 │    19088757828     19083542252    0.03 │   92676    92820   -0.16 │
│             rocq-mathcomp-ssreflect │    1.09     1.11   -1.80 │     7367540506      7366868413    0.01 │  614864   613088    0.29 │
│                           rocq-elpi │   16.70    16.88   -1.07 │   120103885506    120004768236    0.08 │  469832   470680   -0.18 │
│                      rocq-equations │    7.97     8.02   -0.62 │    56245525583     55648247544    1.07 │  400288   402024   -0.43 │
│                        rocq-runtime │   76.71    76.75   -0.05 │   556228959445    555689510224    0.10 │  496620   496620    0.00 │
│                 rocq-mathcomp-order │   93.98    93.58    0.43 │   672132904292    668137189185    0.60 │  921144   920888    0.03 │
│                       coq-fourcolor │ 1361.13  1351.91    0.68 │ 12482380320664  12428344760905    0.43 │ 1013228  1034408   -2.05 │
│ coq-neural-net-interp-computed-lite │  238.51   235.11    1.45 │  2276183672452   2261196279942    0.66 │  857028   847980    1.07 │
│          rocq-metarocq-translations │   16.73    16.47    1.58 │   117236160419    115155038786    1.81 │  913656   888616    2.82 │
│                 rocq-mathcomp-field │  203.76   200.27    1.74 │  1452276096824   1441587416714    0.74 │ 2234580  2229568    0.22 │
│                           rocq-core │    9.14     8.96    2.01 │    64202765502     63183313372    1.61 │  506160   506644   -0.10 │
│         coq-rewriter-perf-SuperFast │  471.94   461.40    2.28 │  3621002218216   3539232291250    2.31 │ 1311252  1317828   -0.50 │
│                    coq-fiat-parsers │  281.15   273.66    2.74 │  2133861248153   2086081897377    2.29 │ 2282400  2259844    1.00 │
│                            coq-hott │  164.99   160.11    3.05 │  1105658912338   1070045554947    3.33 │  470972   475344   -0.92 │
│                       coq-fiat-core │   58.66    56.81    3.26 │   356357363592    340841600489    4.55 │  492496   486668    1.20 │
│                    coq-math-classes │   85.67    82.79    3.48 │   517477693059    497348804202    4.05 │  519660   522572   -0.56 │
│               rocq-mathcomp-algebra │  380.27   367.00    3.62 │  2721865446878   2639288504401    3.13 │ 1571052  1568956    0.13 │
│  rocq-mathcomp-group-representation │  100.18    96.21    4.13 │   683863219632    666104896823    2.67 │ 1536520  1522156    0.94 │
│              rocq-metarocq-template │   85.42    81.92    4.27 │   586258932101    556775523155    5.30 │ 1120512  1120608   -0.01 │
│        coq-fiat-crypto-with-bedrock │ 8621.56  8215.99    4.94 │ 71560456222129  68169709634924    4.97 │ 3981912  4012092   -0.75 │
│              coq-mathcomp-odd-order │  636.79   606.17    5.05 │  4390440514914   4242892970727    3.48 │ 2718572  2720640   -0.08 │
│                  rocq-mathcomp-boot │   41.87    39.83    5.12 │   249277890612    234121315791    6.47 │  669944   669184    0.11 │
│                        coq-coqprime │   59.02    56.10    5.20 │   409542812001    382898558068    6.96 │  833240   831288    0.23 │
│                 rocq-metarocq-utils │   25.75    24.42    5.45 │   166582749263    156301687737    6.58 │  600980   594604    1.07 │
│                         rocq-stdlib │  254.01   240.61    5.57 │  1601597985876   1492399921579    7.32 │  769584   761984    1.00 │
│                         coq-coqutil │   50.80    48.00    5.83 │   316858659609    297440733302    6.53 │  565752   565324    0.08 │
│                        coq-rewriter │  356.02   330.44    7.74 │  2646057701556   2435004632569    8.67 │ 1729472  1498584   15.41 │
│                            coq-corn │  699.06   646.31    8.16 │  4746672525316   4324383722516    9.77 │  825664   634324   30.16 │
│                   coq-iris-examples │  402.18   371.74    8.19 │  2625266154667   2400089534573    9.38 │ 1100080  1096068    0.37 │
│                        rocq-bignums │   27.52    25.38    8.43 │   178231668140    160519436307   11.03 │  492012   462344    6.42 │
│                           coq-color │  251.41   230.95    8.86 │  1613909278820   1448252233910   11.44 │ 1185148  1202544   -1.45 │
│                rocq-metarocq-common │   45.30    41.51    9.13 │   296204315552    266054137908   11.33 │  938756   916304    2.45 │
│                        coq-compcert │  338.71   307.14   10.28 │  2226716450197   1985151527454   12.17 │ 1211808  1200928    0.91 │
│           rocq-metarocq-safechecker │  346.94   311.90   11.23 │  2607295734784   2308591143646   12.94 │ 1889392  1727668    9.36 │
│               rocq-metarocq-erasure │  488.91   439.43   11.26 │  3363098861061   2960058673528   13.62 │ 1986732  1833872    8.34 │
│                 rocq-metarocq-pcuic │  677.55   605.11   11.97 │  4412129840026   3845462136713   14.74 │ 2525884  1699176   48.65 │
│              rocq-mathcomp-solvable │  111.39    98.45   13.14 │   757423905718    660075694710   14.75 │ 1102336  1079672    2.10 │
│                 coq-category-theory │ 1547.34  1354.85   14.21 │ 11391996820490   9835557502540   15.82 │ 6719476  6746844   -0.41 │
│          rocq-mathcomp-finite-group │   30.94    26.75   15.66 │   203744206950    172847095876   17.88 │  590644   572236    3.22 │
│                             coq-vst │ 1006.08   813.13   23.73 │  6981090070356   6063853939751   15.13 │ 6028000  2000780  201.28 │
│          coq-performance-tests-lite │ 1163.11   876.00   32.78 │  9676703856652   6966002824397   38.91 │ 1469480  1571776   -6.51 │
│               coq-engine-bench-lite │  560.74   127.52  339.73 │  4126282141332    940734250323  338.62 │ 1129192  1135916   -0.59 │
└─────────────────────────────────────┴──────────────────────────┴────────────────────────────────────────┴──────────────────────────┘

INFO: failed to install
coq-coquelicot (dependency install failed in NEW)

🐢 Top 25 slow downs
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│                                                   TOP 25 SLOW DOWNS                                                    │
│                                                                                                                        │
│  OLD     NEW      DIFF     %DIFF     Ln                 FILE                                                           │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│   18.1     206  188.1772  1041.77%    32  coq-performance-tests-lite/src/pattern.v.html                                │
│   18.8     207  188.1313   999.59%    31  coq-engine-bench-lite/coq/PerformanceDemos/pattern.v.html                    │
│   10.1    78.1   68.0243   673.31%   325  coq-engine-bench-lite/coq/PerformanceDemos/one_step_reduction.v.html         │
│   10.2    74.8   64.5722   631.71%   214  coq-engine-bench-lite/coq/PerformanceDemos/one_step_reduction.v.html         │
│   8.02    57.6   49.5928   618.03%    31  coq-performance-tests-lite/src/pattern.v.html                                │
│   8.19    57.6   49.4041   603.19%    30  coq-engine-bench-lite/coq/PerformanceDemos/pattern.v.html                    │
│   3.37    43.1   39.7375  1180.32%   109  coq-engine-bench-lite/coq/PerformanceDemos/one_step_reduction.v.html         │
│ 16.178  44.977   28.7990   178.01%  1209  coq-vst/floyd/Component.v.html                                               │
│ 16.043  44.431   28.3880   176.95%  1515  coq-vst/floyd/VSU.v.html                                                     │
│ 16.184  43.345   27.1610   167.83%  1223  coq-vst/floyd/Component.v.html                                               │
│   39.6    66.3   26.7581    67.65%    81  coq-fiat-crypto-with-bedrock/rupicola/src/Rupicola/Examples/Utf8/Utf8.v.html │
│   43.7    65.1   21.3423    48.82%   221  coq-fiat-crypto-with-bedrock/src/Bedrock/P256/Coord32.v.html                 │
│   37.4    56.1   18.6756    49.88%   222  coq-fiat-crypto-with-bedrock/src/Bedrock/P256/Coord32.v.html                 │
│   31.5    47.2   15.6734    49.71%   223  coq-fiat-crypto-with-bedrock/src/Bedrock/P256/Coord32.v.html                 │
│  7.825  22.953   15.1280   193.33%  1512  coq-vst/floyd/VSU.v.html                                                     │
│  8.407  23.069   14.6620   174.40%   755  coq-vst/floyd/Component.v.html                                               │
│  7.813  22.105   14.2920   182.93%  1514  coq-vst/floyd/VSU.v.html                                                     │
│  7.893  22.116   14.2230   180.20%  1222  coq-vst/floyd/Component.v.html                                               │
│  7.949  22.156   14.2070   178.73%  1221  coq-vst/floyd/Component.v.html                                               │
│  8.125  22.119   13.9940   172.23%  1509  coq-vst/floyd/Component.v.html                                               │
│   8.11  21.998   13.8880   171.25%   783  coq-vst/floyd/Component.v.html                                               │
│   3.45    17.1   13.6345   394.95%    30  coq-performance-tests-lite/src/pattern.v.html                                │
│   7.93  21.507   13.5770   171.21%  1208  coq-vst/floyd/Component.v.html                                               │
│   3.50    17.0   13.5291   386.91%    29  coq-engine-bench-lite/coq/PerformanceDemos/pattern.v.html                    │
│  7.959  21.305   13.3460   167.68%  1506  coq-vst/floyd/Component.v.html                                               │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
🐇 Top 25 speed ups
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│                                                                TOP 25 SPEED UPS                                                                 │
│                                                                                                                                                 │
│  OLD      NEW      DIFF     %DIFF    Ln                      FILE                                                                               │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│   63.3     1.94  -61.3298  -96.94%   608  coq-bedrock2/bedrock2/src/bedrock2Examples/lightbulb.v.html                                           │
│   63.1     1.95  -61.1894  -96.91%   608  coq-fiat-crypto-with-bedrock/rupicola/bedrock2/bedrock2/src/bedrock2Examples/lightbulb.v.html         │
│   60.2    0.128  -60.0396  -99.79%   857  rocq-mathcomp-analysis/theories/lebesgue_integral_theory/lebesgue_integral_differentiation.v.html     │
│   49.8   0.0743  -49.7372  -99.85%   376  coq-unimath/UniMath/ModelCategories/Generated/LNWFSMonoidalStructure.v.html                           │
│   44.6     1.20  -43.3990  -97.31%   578  coq-fiat-crypto-with-bedrock/rupicola/bedrock2/compiler/src/compiler/MMIO.v.html                      │
│   41.2     1.22  -40.0303  -97.05%  1423  coq-fiat-crypto-with-bedrock/rupicola/bedrock2/compiler/src/compiler/FlatToRiscvFunctions.v.html      │
│ 29.172    0.056  -29.1160  -99.81%   147  coq-vst/veric/expr_lemmas4.v.html                                                                     │
│   35.4     6.41  -28.9970  -81.90%   898  coq-fiat-crypto-with-bedrock/src/Bedrock/Secp256k1/JoyeLadder.v.html                                  │
│ 28.742    0.019  -28.7230  -99.93%   194  coq-vst/veric/expr_lemmas4.v.html                                                                     │
│   26.6   0.0134  -26.5745  -99.95%   374  coq-unimath/UniMath/ModelCategories/Generated/LNWFSMonoidalStructure.v.html                           │
│   26.5   0.0128  -26.5191  -99.95%   375  coq-unimath/UniMath/ModelCategories/Generated/LNWFSMonoidalStructure.v.html                           │
│   21.4    0.270  -21.1473  -98.74%   338  coq-unimath/UniMath/ModelCategories/Generated/LNWFSMonoidalStructure.v.html                           │
│   17.4    0.310  -17.0614  -98.22%   905  coq-unimath/UniMath/ModelCategories/Generated/LNWFSCocomplete.v.html                                  │
│   20.1     4.50  -15.6136  -77.63%   543  coq-unimath/UniMath/CategoryTheory/Presheaves/SigmaTypes.v.html                                       │
│   12.4   0.0996  -12.2720  -99.19%    97  coq-unimath/UniMath/CategoryTheory/Hyperdoctrines/HValuedSets.v.html                                  │
│   11.4    0.233  -11.1919  -97.96%  1828  rocq-mathcomp-analysis/theories/ftc.v.html                                                            │
│   11.8    0.813  -10.9742  -93.10%   216  coq-fiat-crypto-with-bedrock/src/Fancy/Barrett256.v.html                                              │
│   16.1     6.39   -9.6894  -60.27%   898  coq-fiat-crypto-with-bedrock/src/Bedrock/Secp256k1/JoyeLadder.v.html                                  │
│   9.40    0.231   -9.1704  -97.54%   253  rocq-mathcomp-analysis/theories/gauss_integral.v.html                                                 │
│   9.09   0.0462   -9.0453  -99.49%   127  coq-mathcomp-odd-order/theories/BGappendixAB.v.html                                                   │
│   8.63    0.298   -8.3323  -96.55%   453  coq-unimath/UniMath/SyntheticHomotopyTheory/Circle2.v.html                                            │
│   15.3     8.50   -6.7573  -44.29%     6  coq-fiat-crypto-with-bedrock/rupicola/bedrock2/deps/riscv-coq/src/riscv/Proofs/DecodeEncodeCSR.v.html │
│   7.47    0.729   -6.7459  -90.25%   136  coq-unimath/UniMath/CategoryTheory/Actegories/Examples/ActionOfEndomorphismsInCATElementary.v.html    │
│   6.42  0.00980   -6.4128  -99.85%   853  coq-unimath/UniMath/ModelCategories/Generated/LNWFSCocomplete.v.html                                  │
│   6.08   0.0190   -6.0618  -99.69%   323  coq-unimath/UniMath/RealNumbers/DedekindCuts.v.html                                                   │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

@github-actions github-actions Bot added the needs: rebase Should be rebased on the latest master to solve conflicts or have a newer CI run. label Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs: rebase Should be rebased on the latest master to solve conflicts or have a newer CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants