Skip to content

Conversation

@ppedrot
Copy link
Member

@ppedrot ppedrot commented Oct 29, 2025

We leverage the fact that a ~> unit -> b ≅ a -> b where ~> stands for a hypothetical pure arrow type. This allows replacing all instances of the NonLogical.t monad in the logic monad type with basically nothing, leaving effects implicits in the rightmost arrow type. Since all clients evaluate the thunk directly, the new code should be equivalent to the previous one.

Actually, it may even be more correct given that we already implicitly use the function space in the monadic bind operator to perform side-effects.

@ppedrot ppedrot added request: full CI Use this label when you want your next push to trigger a full CI. kind: experiment labels Oct 29, 2025
@coqbot-app coqbot-app bot removed the request: full CI Use this label when you want your next push to trigger a full CI. label Oct 29, 2025
@ppedrot
Copy link
Member Author

ppedrot commented Oct 29, 2025

@coqbot bench

@coqbot-app
Copy link
Contributor

coqbot-app bot commented Oct 30, 2025

🏁 Bench results:

┌─────────────────────────────────────┬─────────────────────────┬───────────────────────────────────────┬─────────────────────────┐
│                                     │      user time [s]      │           CPU instructions            │  max resident mem [KB]  │
│                                     │                         │                                       │                         │
│            package_name             │   NEW      OLD    PDIFF │      NEW             OLD        PDIFF │   NEW      OLD    PDIFF │
├─────────────────────────────────────┼─────────────────────────┼───────────────────────────────────────┼─────────────────────────┤
│                           rocq-core │    6.47     6.56  -1.37 │    40743918125     40779748076  -0.09 │  455444   455112   0.07 │
│                           rocq-elpi │   15.42    15.58  -1.03 │   107542835280    107546619490  -0.00 │  560140   560472  -0.06 │
│               rocq-metarocq-erasure │  472.20   475.63  -0.72 │  3239987501263   3243375719894  -0.10 │ 1924216  1906400   0.93 │
│                      rocq-equations │    8.54     8.59  -0.58 │    59283456879     59304300657  -0.04 │  397268   397272  -0.00 │
│          rocq-metarocq-translations │   15.49    15.58  -0.58 │   109827450508    109834509801  -0.01 │  772308   771688   0.08 │
│                  rocq-mathcomp-boot │   38.18    38.40  -0.57 │   226228535684    226226933978   0.00 │  659904   659916  -0.00 │
│                           coq-verdi │   42.36    42.60  -0.56 │   283700256867    284063691459  -0.13 │  522224   527344  -0.97 │
│                      coq-coquelicot │   43.87    44.11  -0.54 │   261667583124    262460227365  -0.30 │  840168   834304   0.70 │
│                      coq-verdi-raft │  494.99   496.82  -0.37 │  3438048306140   3445248876464  -0.21 │  818972   821272  -0.28 │
│             rocq-mathcomp-character │   92.58    92.88  -0.32 │   648046686785    648291648972  -0.04 │ 1623544  1623544   0.00 │
│              rocq-mathcomp-fingroup │   25.69    25.76  -0.27 │   167712232839    167715007931  -0.00 │  565600   565284   0.06 │
│                   coq-iris-examples │  368.54   369.45  -0.25 │  2440525652961   2442556937484  -0.08 │ 1142752  1146668  -0.34 │
│                    coq-math-classes │   82.77    82.97  -0.24 │   501641982060    502823168879  -0.23 │  516888   518240  -0.26 │
│                         rocq-stdlib │  438.87   439.74  -0.20 │  1532355222677   1534162566131  -0.12 │  626332   626680  -0.06 │
│                            coq-hott │  157.68   157.97  -0.18 │  1087022846560   1087224911424  -0.02 │  469084   467920   0.25 │
│                        rocq-bignums │   24.92    24.96  -0.16 │   157912605151    158089885414  -0.11 │  455336   457148  -0.40 │
│                        coq-compcert │  296.13   296.23  -0.03 │  1943342657935   1946219621250  -0.15 │ 1160076  1188444  -2.39 │
│                       coq-fourcolor │ 1345.12  1345.22  -0.01 │ 12416739813936  12417200215627  -0.00 │  968620   966972   0.17 │
│                        coq-coqprime │   52.56    52.54   0.04 │   359446112792    359626544473  -0.05 │  822316   820288   0.25 │
│ coq-neural-net-interp-computed-lite │  236.62   236.50   0.05 │  2264608592845   2264750094673  -0.01 │  848612   846096   0.30 │
│              rocq-mathcomp-solvable │   94.87    94.81   0.06 │   647914981016    648073937434  -0.02 │ 1119300  1121252  -0.17 │
│               coq-engine-bench-lite │  125.51   125.40   0.09 │   942046134794    938974906778   0.33 │  990608   990816  -0.02 │
│              rocq-metarocq-template │   81.88    81.78   0.12 │   562110957404    562309054249  -0.04 │ 1025180  1025308  -0.01 │
│              coq-mathcomp-odd-order │  570.24   569.53   0.12 │  4103221502519   4104475647949  -0.03 │ 2621232  2633180  -0.45 │
│          coq-performance-tests-lite │  900.77   899.31   0.16 │  7270866803549   7270453033094   0.01 │ 2194276  2194444  -0.01 │
│                 rocq-mathcomp-order │   84.19    84.02   0.20 │   607598673279    607666823055  -0.01 │ 1540688  1539552   0.07 │
│                           coq-color │  230.16   229.64   0.23 │  1455659219347   1457109164466  -0.10 │ 1132020  1128648   0.30 │
│                        coq-rewriter │  340.41   339.64   0.23 │  2530917529668   2533284356063  -0.09 │ 1283812  1273152   0.84 │
│                             coq-vst │  840.11   838.14   0.24 │  6377217420313   6378301768401  -0.02 │ 2008128  2210376  -9.15 │
│                            coq-corn │  666.70   664.99   0.26 │  4554150484832   4557362630752  -0.07 │  731276   731796  -0.07 │
│                         coq-unimath │ 1781.21  1776.17   0.28 │ 14822510616312  14824794458745  -0.02 │ 1067168  1065344   0.17 │
│                 rocq-mathcomp-field │  170.75   170.25   0.29 │  1283750966428   1283701636915   0.00 │ 2038104  2045864  -0.38 │
│                    coq-fiat-parsers │  275.37   274.55   0.30 │  2113281128526   2115607508957  -0.11 │ 2349500  2351840  -0.10 │
│                         coq-coqutil │   46.39    46.25   0.30 │   289205704268    289636111737  -0.15 │  560300   562952  -0.47 │
│               coq-mathcomp-analysis │  926.62   923.36   0.35 │  6864455516813   6865007068404  -0.01 │ 1966640  1966668  -0.00 │
│                        rocq-runtime │   74.12    73.84   0.38 │   534310003480    534357067890  -0.01 │  510072   510644  -0.11 │
│           rocq-metarocq-safechecker │  316.16   314.80   0.43 │  2360220755129   2361098553870  -0.04 │ 1871808  1863916   0.42 │
│                 coq-category-theory │  581.94   579.32   0.45 │  4292131163794   4292028199524   0.00 │  959996   969416  -0.97 │
│                rocq-metarocq-common │   41.23    41.04   0.46 │   266224291225    266698581969  -0.18 │  893316   912288  -2.08 │
│                 rocq-metarocq-pcuic │  629.71   626.73   0.48 │  3998605778169   4003864646536  -0.13 │ 1879516  1879684  -0.01 │
│         coq-rewriter-perf-SuperFast │  481.39   478.94   0.51 │  3757528342293   3758762035665  -0.03 │ 1250272  1266308  -1.27 │
│               rocq-mathcomp-algebra │  299.75   298.02   0.58 │  2203810350066   2203807015650   0.00 │ 1488996  1499824  -0.72 │
│                       coq-fiat-core │   54.76    54.38   0.70 │   334363773984    334827567907  -0.14 │  481516   483644  -0.44 │
│                 rocq-metarocq-utils │   23.77    23.60   0.72 │   153635298011    153723534887  -0.06 │  586688   586092   0.10 │
│                            coq-core │    2.96     2.87   3.14 │    19667668294     19667936199  -0.00 │  104272   104480  -0.20 │
│             rocq-mathcomp-ssreflect │    1.09     1.05   3.81 │     7193964625      7196998793  -0.04 │  598436   598404   0.01 │
└─────────────────────────────────────┴─────────────────────────┴───────────────────────────────────────┴─────────────────────────┘

INFO: failed to install
coq-bedrock2 (in NEW)
coq-fiat-crypto-with-bedrock (in NEW)

🐢 Top 25 slow downs
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│                                                         TOP 25 SLOW DOWNS                                                          │
│                                                                                                                                    │
│   OLD     NEW     DIFF     %DIFF     Ln                   FILE                                                                     │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│    95.3    96.6  1.2699      1.33%   999  coq-performance-tests-lite/src/fiat_crypto_via_setoid_rewrite_standalone.v.html          │
│    95.5    96.7  1.1811      1.24%   968  coq-performance-tests-lite/src/fiat_crypto_via_setoid_rewrite_standalone.v.html          │
│    36.4    37.0  0.6153      1.69%   139  coq-fiat-parsers/src/Parsers/Refinement/SharpenedJSON.v.html                             │
│    3.12    3.65  0.5309     17.03%   196  rocq-stdlib/theories/ZArith/ZModOffset.v.html                                            │
│    16.1    16.6  0.4573      2.83%    31  coq-engine-bench-lite/coq/PerformanceDemos/pattern.v.html                                │
│    38.1    38.4  0.3773      0.99%   224  coq-performance-tests-lite/PerformanceExperiments/rewrite_lift_lets_map.v.html           │
│  33.572  33.942  0.3700      1.10%   194  coq-vst/veric/expr_lemmas4.v.html                                                        │
│  34.578  34.934  0.3560      1.03%   147  coq-vst/veric/expr_lemmas4.v.html                                                        │
│    38.7    39.0  0.3421      0.88%   236  coq-rewriter/src/Rewriter/Rewriter/Examples/PerfTesting/LiftLetsMap.v.html               │
│ 0.00225   0.336  0.3339  14871.05%   152  coq-mathcomp-analysis/theories/trigo.v.html                                              │
│    1.16    1.50  0.3339     28.70%  1142  rocq-stdlib/theories/FSets/FMapAVL.v.html                                                │
│    11.5    11.8  0.3234      2.81%   324  coq-unimath/UniMath/CategoryTheory/Hyperdoctrines/PartialEqRels/Logic/Existential.v.html │
│    11.5    11.8  0.3126      2.72%   388  coq-unimath/UniMath/CategoryTheory/Hyperdoctrines/PartialEqRels/Logic/Existential.v.html │
│    1.55    1.83  0.2842     18.37%   313  rocq-stdlib/theories/Strings/Byte.v.html                                                 │
│    1.06   1.343  0.2830     26.70%   937  coq-vst/veric/binop_lemmas2.v.html                                                       │
│    18.8    19.1  0.2777      1.47%   481  coq-verdi-raft/theories/RaftProofs/EndToEndLinearizability.v.html                        │
│   0.928    1.20  0.2720     29.29%   408  rocq-stdlib/theories/MSets/MSetAVL.v.html                                                │
│    16.2    16.5  0.2655      1.64%    32  coq-performance-tests-lite/src/pattern.v.html                                            │
│  0.0201   0.282  0.2621   1306.66%   384  coq-mathcomp-analysis/theories/topology_theory/compact.v.html                            │
│   0.386   0.648  0.2620     67.93%   249  rocq-stdlib/theories/Structures/OrdersEx.v.html                                          │
│   0.150   0.400  0.2499    166.92%   592  rocq-stdlib/theories/MSets/MSetAVL.v.html                                                │
│   0.348   0.588  0.2394     68.79%     1  rocq-stdlib/theories/Zmod/ZstarDef.v.html                                                │
│    4.55    4.78  0.2340      5.14%  2861  rocq-metarocq-safechecker/safechecker/theories/PCUICTypeChecker.v.html                   │
│   0.319   0.552  0.2336     73.28%    17  rocq-stdlib/theories/micromega/Env.v.html                                                │
│    24.9    25.1  0.2214      0.89%    12  coq-fourcolor/theories/proof/job227to230.v.html                                          │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
🐇 Top 25 speed ups
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│                                                              TOP 25 SPEED UPS                                                               │
│                                                                                                                                             │
│  OLD     NEW      DIFF     %DIFF    Ln                     FILE                                                                             │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│  1.81      1.14  -0.6646  -36.75%   330  rocq-metarocq-erasure/erasure/theories/Typed/ExtractionCorrectness.v.html                          │
│  7.61      7.03  -0.5753   -7.56%   604  coq-unimath/UniMath/CategoryTheory/EnrichedCats/Colimits/Examples/StructureEnrichedColimits.v.html │
│  1.44      1.08  -0.3577  -24.83%    73  rocq-stdlib/theories/Numbers/HexadecimalString.v.html                                              │
│ 0.298   0.00117  -0.2967  -99.61%   202  coq-mathcomp-analysis/theories/normedtype_theory/ereal_normedtype.v.html                           │
│  11.6      11.3  -0.2936   -2.54%   410  coq-verdi-raft/theories/RaftProofs/LeaderLogsLogMatchingProof.v.html                               │
│  1.46      1.17  -0.2835  -19.47%   813  rocq-stdlib/theories/MSets/MSetRBT.v.html                                                          │
│  8.56      8.29  -0.2725   -3.18%  1331  coq-mathcomp-odd-order/theories/PFsection9.v.html                                                  │
│ 1.982     1.714  -0.2680  -13.52%   411  coq-vst/veric/mapsto_memory_block.v.html                                                           │
│ 0.265  0.000474  -0.2641  -99.82%   383  coq-mathcomp-analysis/theories/topology_theory/compact.v.html                                      │
│ 0.426     0.171  -0.2549  -59.84%   374  rocq-stdlib/theories/Sorting/SetoidList.v.html                                                     │
│ 0.603     0.353  -0.2505  -41.52%    17  rocq-stdlib/theories/Logic/IndefiniteDescription.v.html                                            │
│  7.65      7.40  -0.2484   -3.25%   602  coq-unimath/UniMath/CategoryTheory/EnrichedCats/Limits/Examples/StructureEnrichedLimits.v.html     │
│  2.39      2.14  -0.2431  -10.19%   382  coq-unimath/UniMath/CategoryTheory/Hyperdoctrines/PartialEqRels/PERCategory.v.html                 │
│ 0.527     0.292  -0.2351  -44.58%    11  rocq-stdlib/theories/ZArith/Zdiv_facts.v.html                                                      │
│  28.3      28.1  -0.2332   -0.82%    12  coq-fourcolor/theories/proof/job107to164.v.html                                                    │
│  3.04      2.81  -0.2280   -7.51%   509  coq-unimath/UniMath/CategoryTheory/Hyperdoctrines/PartialEqRels/ExponentialLam.v.html              │
│  10.1      9.85  -0.2238   -2.22%   279  coq-category-theory/Theory/Metacategory.v.html                                                     │
│ 0.222  0.000182  -0.2222  -99.92%   233  rocq-mathcomp-field/field/closed_field.v.html                                                      │
│ 0.221  0.000561  -0.2200  -99.75%   558  rocq-mathcomp-field/field/closed_field.v.html                                                      │
│ 0.227   0.00864  -0.2189  -96.20%   336  coq-mathcomp-odd-order/theories/BGsection9.v.html                                                  │
│ 0.468     0.250  -0.2180  -46.59%    11  rocq-stdlib/theories/Strings/HexString.v.html                                                      │
│  4.87      4.65  -0.2152   -4.42%   121  coq-unimath/UniMath/CategoryTheory/Hyperdoctrines/PartialEqRels/ExponentialEqs.v.html              │
│ 0.216   0.00103  -0.2152  -99.52%   512  rocq-metarocq-safechecker/safechecker/theories/PCUICSafeChecker.v.html                             │
│ 0.534     0.321  -0.2137  -40.01%     1  rocq-stdlib/theories/ZArith/ZNsatz.v.html                                                          │
│ 0.220   0.00746  -0.2123  -96.60%   403  coq-mathcomp-odd-order/theories/BGsection5.v.html                                                  │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

@Janno
Copy link
Contributor

Janno commented Nov 5, 2025

I think there is a (perhaps) unintended change here that allows Ltac2 panics (Control.throw) to break out of typeclass search more than before. I had been under the impression that there was a conscious design decision to not let this happen at all and I have written code under that assumption. But I am not sure I can point to why I got this impression, except for the fact that I have never seen a panic escape typeclass search until today. While trying to minimize the build failure that came out of me trying to run performance tests on this PR (SkyLabsAI#2) I noticed that the behavior is actually inconsistent on master

AFAICT this PR changes only the behavior of Ltac2 panics produced in Hint Externs using tactic-in-terms. The behavior now matches the behavior of calling ltac2 directly or indirectly through ltac1 in a Hint Extern: typeclass search aborts entirely.

Require Import Ltac2.Ltac2.
Class Nothing := {}.
Instance default : Nothing | 1 := {}.
Module Ltac2Direct.
  Hint Extern 0 Nothing => Control.throw_invalid_argument "" : typeclass_instances.
  Goal Nothing.
    Fail ltac1:(typeclasses eauto). (* uncaught exception on 9.1 and PR *)
  Abort.
End Ltac2Direct.
Set Default Proof Mode "Classic".
Module Ltac1Ltac2.
  Ltac test := ltac2:(Control.throw_invalid_argument "").
  Hint Extern 0 Nothing =>  test : typeclass_instances.
  Goal Nothing.
    Fail typeclasses eauto. (* uncaught exception on master and PR *)
  Abort.
End Ltac1Ltac2.
Module Ltac2InTerm.
  Ltac2 test () := Control.throw_invalid_argument "".
  Hint Extern 0 Nothing => refine ltac2:(test ()) : typeclass_instances.
  Goal Nothing.
    typeclasses eauto. (* uncaught exception only in PR *)
  Abort.
End Ltac2InTerm.
Module Ltac1Ltac2InTerm.
  Ltac test := ltac2:(Control.throw_invalid_argument "").
  Hint Extern 0 Nothing => refine ltac:(test) : typeclass_instances.
  Goal Nothing.
    typeclasses eauto. (* uncaught exception only in PR *)
  Abort.
End Ltac1Ltac2InTerm.

@ppedrot
Copy link
Member Author

ppedrot commented Nov 7, 2025

@Janno it's not clear to me what's the intended semantics of the interaction between backtracking and IO, but I'd have naively expected the converse from TC resolution. That is, panics always abort the search and propagate to toplevel, regardless of their origin. In any case, the behaviour should be uniform, even if we decided to catch panics thrown by extern hints in TC search.

@Janno
Copy link
Contributor

Janno commented Nov 7, 2025

I agree with the expectation that panics should always bubble up to the top-level and it seems to me like this PR would achieve this. I cannot come up with another constellation outside of tactics in terms in Hint Extern that results in caught panics.

Comment on lines 116 to 119
let run = fun x ->
try x () with Exception e as src ->
let (src, info) = Exninfo.capture src in
Exninfo.iraise (e, info)
Copy link
Contributor

Choose a reason for hiding this comment

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

IIUC the change is because we stop calling this in Proofview.apply, so the Logic_monad.Exception isn't turned into whatever exn we gave to throw, and Proofview.Goal.enter doesn't catch Logic_monad.Exception but did catch the throw exn (it doesn't check fatal_flag, and can't since fatal_flag is currently internal to ltac2).

There is no real point in exposing this implementation detail, all callers
immediately evaluate the resulting thunk.
We leverage the fact that a ~> unit -> b ≅ a -> b where ~> stands
for a hypothetical pure arrow type. This allows replacing all
instances of the NonLogical.t monad in the logic monad type with
basically nothing, leaving effects implicits in the rightmost
arrow type. Since all clients evaluate the thunk directly, the
new code should be equivalent to the previous one.

Actually, it may even be more correct given that we already
implicitly use the function space in the monadic bind operator
to perform side-effects.
@ppedrot ppedrot added the request: full CI Use this label when you want your next push to trigger a full CI. label Nov 18, 2025
@ppedrot ppedrot force-pushed the logic-monad-collapse-nonlogical branch from 79616bc to dd3766e Compare November 18, 2025 17:04
@coqbot-app coqbot-app bot removed the request: full CI Use this label when you want your next push to trigger a full CI. label Nov 18, 2025
@ppedrot
Copy link
Member Author

ppedrot commented Nov 18, 2025

I tweaked Proofview.apply accordingly. The new behaviour should be unchanged, up to the fact that it is now really legal to call effectful thunks in monadic binds.

{ iolist : 'r. 'i ~> ('e -> 'r) ~> ('a ~> 'o ~> ('e -> 'r) -> 'r) -> 'r }
Alternatively we could use records but then it would incur a runtime
overhead.
Copy link
Contributor

Choose a reason for hiding this comment

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

There is another possibility: tuples.

Indeed, as far as the OCaml compiler is concerned, there is no difference between a function taking a single tuple argument and a function taking several arguments. For example, the same machine code is generated for f and g below. (Said otherwise, f actually receives 4 separate arguments, in the same order as g.)

let f (a,b,c,d) = a + b + c + d
let g a b c d = a + b + c + d

So, there is no runtime overhead when using tuples. (Obviously, there is some overhead whenever the caller of f needs to destruct the tuple argument on the fly. But it only happens if the argument comes from outside the caller of f. If the tuple is created before the call to f, the compiler recognizes it and optimizes it away.)

In case you wonder how it can even work when f is used as a closure, that is where the infamous caml_tuplify function comes into play. And since caml_tuplify is generally faster than caml_curry (because the resulting closure cannot be partially applied), there is no runtime overhead either when using closures.

Copy link
Member Author

Choose a reason for hiding this comment

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

I checked on return and the OCaml compiler doesn't seem to be able to perform this analysis. It does allocate a tuple and performs projections here and there. It's not so surprising either, all these functions are only known at runtime and there is no way it is going to inline all of them... So maybe it helps in some cases, but most of the combinators defined in this file are not going to be optimized.

We discussed the matter with OCaml experts recently, and it seems that OxCaml lets you write such "pure functions" in a way that is tracked by the type system. Basically you have to write a tuplified function and annotate the type with unboxed to achieve this.

Copy link
Contributor

Choose a reason for hiding this comment

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

I do not understand. Are you talking about this kind of code?

type ('a, 'i, 'o, 'e) t = { iolist : 'r. 'i * ('e -> 'r) * ('a -> 'o -> ('e -> 'r) -> 'r) -> 'r }

let return x = { iolist = fun (s, nil, cons) -> cons x s nil }

The OCaml compiler does not perform any projection inside the closure stored in iolist. As I explained, it produces the exact same code as if fun s nil cons -> cons x s nil had been written.

Copy link
Member Author

Choose a reason for hiding this comment

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

No, I also turned the inner arrow type into tuples.

type ('a, 'i, 'o, 'e) t =
  { iolist : 'r. ('i * ('e -> 'r) * ('a * 'o * ('e -> 'r) -> 'r)) -> 'r }

let return x =
  { iolist = fun (s, nil, cons) -> cons (x, s, nil) }

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. In that case, there will indeed be some overhead. But do you actually need to turn the inner arrow type into a tuple? You should have complete control over all the callers of this inner function since the type of iolist is not exported. So, you can make sure that it is never partially applied.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants