Fuse unionWith and checkBinRel in PlutusLedgerApi.V1.Data.Value#7799
Merged
Conversation
Contributor
Execution Budget Golden Diffoutputplutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/geq1.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/geq2.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/geq3.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/geq4.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/geq5.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/gt1.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/gt2.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/gt3.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/gt4.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/gt5.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.6/geq1.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.6/geq2.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.6/geq3.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.6/geq4.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.6/geq5.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.6/gt1.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.6/gt2.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.6/gt3.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.6/gt4.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.6/gt5.golden.eval
This comment will get updated when changes are made. |
5282416 to
178738a
Compare
SeungheonOh
approved these changes
Jun 2, 2026
SeungheonOh
left a comment
Collaborator
There was a problem hiding this comment.
Nice optimization. maybe it could be faster if Map.all gets inlined as well but I'm not sure if that will be worth it
Drop the internal unionVal and checkPred helpers; inline the merge logic into unionWith and checkBinRel respectively. The previous chain built a Map CurrencySymbol (Map TokenName (These Integer Integer)) intermediate via unionVal, then re-walked it to apply f -- three outer passes for a single conceptual merge. The fused unionWith now runs Map.union once and a single outer Map.map, collapsing the These shape inline per currency-symbol entry. The Map TokenName (These Integer Integer) stage is gone; the outer Map.map runs once instead of twice. checkBinRel is refactored along the same shape with Map.union + Map.all, which gives geq / leq / gt / lt short-circuit termination on the first failing pair. Adds Spec.Data.Value.test_unionWith: a QuickCheck property that compiles unionWith via TH, evaluates on the CEK machine, and compares against the host-Haskell unionWith for the same inputs. Differential test against the Plinth compiler: any divergence is a compilation bug, not a semantics bug. The Spec.Data.Budget gt / geq budget goldens are regenerated: short-circuit checkBinRel reduces gt4 / geq4 by ~46% (the worst-case adverse input); other shapes drop 0.7-3% from removing one outer pass over the These intermediate. The remaining diff is the cost-model anchor; that component is unrelated to this change. Budget evidence (union matrix vs builtin, unsafeDataAsValue baseline) lives on the companion experimental branch yura/issue-2243-fused-unionwith-evidence, stacked on the sibling valueOf-evidence branch. For IntersectMBO/plutus-private#2243.
The previous commit (642442a) regenerated the GHC 9.6 column only. plutus-ledger-api-plugin-test is also buildable on GHC 9.12 per the ghc-version-support common stanza, and Hydra runs it there; the checkBinRel rewrite changes the compiled UPLC enough to move the budgets in both columns. Same regen, run in nix develop .#ghc912. For IntersectMBO/plutus-private#2243.
Compare the typed unionWith (+) against the builtin unionValue path on CEK rather than against host-Haskell unionWith: a shared-source oracle cannot catch a bug that lands the same way on both sides. Inputs are restricted to the well-formed domain unsafeDataAsValue accepts, and results are compared up to key order and zero-sum entries. Bindings use plinthc instead of the compile splice. Also rephrase Note [Single-pass unionWith] and the checkBinRel docstring to describe the present structure without contrasting against history. For IntersectMBO/plutus-private#2243.
The fusion is a constant-factor change, not the elimination of the These intermediate it was described as. Map.union still produces a per-key These and the inner These is still built transiently for shared keys; what the fusion removes is one of the two outer Map.map passes over the post-union map, plus the wrap-then-remap of single-side currencies. Adjust the changelog and drop Note [Single-pass unionWith] accordingly; the merge structure is plain from the code. For IntersectMBO/plutus-private#2243.
178738a to
4a69249
Compare
Contributor
Author
|
Thanks. Leaving this out of the PR for now. |
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.
PlutusLedgerApi.V1.Data.Value.unionWithnow runs one outerMap.unionand one outerMap.mapthat collapses theTheseshape inline per currency-symbol entry. The previous implementation built aMap CurrencySymbol (Map TokenName (These Integer Integer))intermediate via aunionValhelper and then re-walked it to applyf: three outer passes for a single conceptual merge.checkBinRelis refactored along the same shape withMap.union+Map.all; the inner per-currency merge now sits inside the short-circuitingMap.allwalk, so a failing pair skips the inner merges of every later currency (geq/leq/gt/ltinherit the early exit).unionValandcheckPredare dropped (both were module-internal, not exported). Same semantics, no public API change.The
Map.unionlookup-and-merge core (O(L×R)) is untouched, so this is a constant-factor change: one of the two outerMap.mappasses over the post-union map goes away, and a currency present on only one side has its token map mapped once instead of wrapped and then re-mapped.Closes plutus-private#2243.