Skip to content

Use UArray instead of Set for builtin availability check#7736

Open
zeme-wana wants to merge 1 commit intomasterfrom
naughty-tesla-3c6207
Open

Use UArray instead of Set for builtin availability check#7736
zeme-wana wants to merge 1 commit intomasterfrom
naughty-tesla-3c6207

Conversation

@zeme-wana
Copy link
Copy Markdown
Collaborator

@zeme-wana zeme-wana commented Apr 23, 2026

Summary

Addresses the TODO in SerialisedScript.hs:202 suggesting a better data structure for the builtin availability check in scriptCBORDecoder.

Previously checkBuiltin did an O(log n) Set.member on a Set DefaultFun. DefaultFun already derives Ix, so a UArray DefaultFun Bool gives a true O(1) unboxed array index with no fromEnum/hash at the lookup site (GHC's Ix instance handles the offset).

The builtin availability check in `scriptCBORDecoder` was using
`Set DefaultFun` for O(log n) membership, with a TODO suggesting
`IntSet`. A `UArray DefaultFun Bool` is a better fit: since
`DefaultFun` derives `Ix`, lookup is a true O(1) unboxed array index
with no conversion at the lookup site.

The array is built once per call via `runSTUArray`, folding over the
`Set` through its `Foldable` instance (no intermediate list). The
array covers all ~100 `DefaultFun` constructors.
@zeme-wana zeme-wana self-assigned this Apr 23, 2026
@zeme-wana zeme-wana added the No Changelog Required Add this to skip the Changelog Check label Apr 23, 2026
@zliu41
Copy link
Copy Markdown
Member

zliu41 commented Apr 23, 2026

Unclear this is an improvement. Try validation-decode and validation-full benchmarks.

@zeme-wana zeme-wana requested review from Unisay and zliu41 April 23, 2026 13:56
@zeme-wana
Copy link
Copy Markdown
Collaborator Author

/benchmark validation-decode

@zeme-wana
Copy link
Copy Markdown
Collaborator Author

/benchmark validation-full

@github-actions
Copy link
Copy Markdown
Contributor

Click here to check the status of your benchmark.

@github-actions
Copy link
Copy Markdown
Contributor

Comparing benchmark results of 'validation-decode' on '0468c1c57d' (base) and 'f2742c451c' (PR)

Results table
Script 0468c1c f2742c4 Change
auction_1-1 201.3 μs 186.8 μs -7.2%
auction_1-2 547.7 μs 571.9 μs +4.4%
auction_1-3 547.4 μs 569.6 μs +4.1%
auction_1-4 201.0 μs 186.8 μs -7.1%
auction_2-1 200.3 μs 186.4 μs -6.9%
auction_2-2 547.4 μs 569.1 μs +4.0%
auction_2-3 545.5 μs 569.8 μs +4.5%
auction_2-4 547.3 μs 568.8 μs +3.9%
auction_2-5 201.3 μs 186.1 μs -7.6%
coop-1 114.0 μs 106.5 μs -6.6%
coop-2 253.1 μs 241.6 μs -4.5%
coop-3 251.7 μs 243.1 μs -3.4%
coop-4 297.2 μs 287.0 μs -3.4%
coop-5 297.5 μs 288.4 μs -3.1%
coop-6 133.0 μs 124.3 μs -6.5%
coop-7 132.9 μs 124.1 μs -6.6%
crowdfunding-success-1 243.8 μs 256.0 μs +5.0%
crowdfunding-success-2 243.3 μs 229.3 μs -5.8%
crowdfunding-success-3 242.4 μs 229.8 μs -5.2%
currency-1 238.6 μs 225.3 μs -5.6%
escrow-redeem_1-1 322.9 μs 312.5 μs -3.2%
escrow-redeem_1-2 322.4 μs 305.3 μs -5.3%
escrow-redeem_2-1 322.8 μs 305.8 μs -5.3%
escrow-redeem_2-2 322.4 μs 303.9 μs -5.7%
escrow-redeem_2-3 322.4 μs 304.8 μs -5.5%
escrow-refund-1 321.1 μs 304.6 μs -5.1%
future-increase-margin-1 237.8 μs 226.6 μs -4.7%
future-increase-margin-2 326.9 μs 307.6 μs -5.9%
future-increase-margin-3 327.2 μs 311.4 μs -4.8%
future-increase-margin-4 688.0 μs 659.2 μs -4.2%
future-increase-margin-5 687.4 μs 664.2 μs -3.4%
future-pay-out-1 237.6 μs 224.8 μs -5.4%
future-pay-out-2 326.4 μs 308.2 μs -5.6%
future-pay-out-3 326.0 μs 308.0 μs -5.5%
future-pay-out-4 684.3 μs 658.9 μs -3.7%
future-settle-early-1 237.5 μs 226.6 μs -4.6%
future-settle-early-2 325.4 μs 307.7 μs -5.4%
future-settle-early-3 327.2 μs 308.6 μs -5.7%
future-settle-early-4 685.9 μs 659.8 μs -3.8%
game-sm-success_1-1 529.4 μs 508.9 μs -3.9%
game-sm-success_1-2 171.8 μs 160.3 μs -6.7%
game-sm-success_1-3 527.7 μs 510.1 μs -3.3%
game-sm-success_1-4 172.0 μs 159.7 μs -7.2%
game-sm-success_2-1 528.7 μs 508.8 μs -3.8%
game-sm-success_2-2 172.4 μs 159.9 μs -7.3%
game-sm-success_2-3 530.0 μs 509.3 μs -3.9%
game-sm-success_2-4 172.9 μs 160.3 μs -7.3%
game-sm-success_2-5 529.8 μs 508.4 μs -4.0%
game-sm-success_2-6 172.7 μs 159.6 μs -7.6%
guardrail-sorted-large 115.7 μs 116.7 μs +0.9%
guardrail-sorted-small 115.8 μs 116.4 μs +0.5%
guardrail-unsorted-large 114.8 μs 111.6 μs -2.8%
guardrail-unsorted-small 115.1 μs 111.7 μs -3.0%
multisig-sm-01 588.0 μs 571.0 μs -2.9%
multisig-sm-02 587.3 μs 571.0 μs -2.8%
multisig-sm-03 588.5 μs 572.2 μs -2.8%
multisig-sm-04 587.6 μs 566.6 μs -3.6%
multisig-sm-05 585.6 μs 570.5 μs -2.6%
multisig-sm-06 587.1 μs 565.2 μs -3.7%
multisig-sm-07 586.4 μs 569.9 μs -2.8%
multisig-sm-08 586.2 μs 568.2 μs -3.1%
multisig-sm-09 588.4 μs 570.0 μs -3.1%
multisig-sm-10 586.1 μs 566.3 μs -3.4%
ping-pong-1 493.1 μs 471.5 μs -4.4%
ping-pong-2 493.0 μs 471.1 μs -4.4%
ping-pong_2-1 493.1 μs 471.5 μs -4.4%
prism-1 166.9 μs 154.7 μs -7.3%
prism-2 520.8 μs 499.6 μs -4.1%
prism-3 242.3 μs 227.0 μs -6.3%
pubkey-1 173.5 μs 162.1 μs -6.6%
stablecoin_1-1 877.8 μs 843.0 μs -4.0%
stablecoin_1-2 173.1 μs 160.5 μs -7.3%
stablecoin_1-3 876.6 μs 840.6 μs -4.1%
stablecoin_1-4 172.6 μs 159.6 μs -7.5%
stablecoin_1-5 876.6 μs 850.5 μs -3.0%
stablecoin_1-6 173.1 μs 159.8 μs -7.7%
stablecoin_2-1 878.0 μs 842.3 μs -4.1%
stablecoin_2-2 172.7 μs 160.4 μs -7.1%
stablecoin_2-3 875.3 μs 843.0 μs -3.7%
stablecoin_2-4 172.8 μs 159.9 μs -7.5%
token-account-1 239.8 μs 225.4 μs -6.0%
token-account-2 226.8 μs 206.2 μs -9.1%
uniswap-1 242.8 μs 227.7 μs -6.2%
uniswap-2 240.3 μs 224.3 μs -6.7%
uniswap-3 746.0 μs 703.3 μs -5.7%
uniswap-4 207.3 μs 173.0 μs -16.5%
uniswap-5 744.3 μs 703.4 μs -5.5%
uniswap-6 185.6 μs 173.2 μs -6.7%
vesting-1 330.9 μs 312.7 μs -5.5%
0468c1c f2742c4 Change
TOTAL 34.17 ms 32.88 ms -3.8%

@github-actions
Copy link
Copy Markdown
Contributor

Click here to check the status of your benchmark.

Copy link
Copy Markdown
Contributor

@Unisay Unisay left a comment

Choose a reason for hiding this comment

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

Nice cleanup on the TODO.

Worth noting that the lookup is O(1) now, but the array itself gets rebuilt on every scriptCBORDecoder call. (ll, pv) ranges over a small finite set (four ledger languages times the handful of known protocol versions), so caching the array at module level costs almost nothing. Each decode then does a Map lookup instead of newArray plus a mapM_ over the Set.

Roughly:

availableBuiltinsArr
  :: PlutusLedgerLanguage -> MajorProtocolVersion -> UArray DefaultFun Bool
availableBuiltinsArr =
  let cache = Map.fromList
        [((ll, pv), build ll pv) | ll <- [minBound .. maxBound], pv <- knownPVs]
      build ll pv = runSTUArray $ do
        arr <- newArray (minBound, maxBound) False
        mapM_ (\f -> writeArray arr f True) (builtinsAvailableIn ll pv)
        pure arr
  in \ll pv -> Map.findWithDefault emptyArr (ll, pv) cache

MajorProtocolVersion doesn't have a meaningful Bounded instance, so knownPVs has to be listed by hand from Common/Versions.hs: alonzoPV, vasilPV, changPV, plominPV, vanRossemPV.

Whichever variant lands, it would be nice to see the win show up in plutus-benchmark:validation numbers before merging.

@github-actions
Copy link
Copy Markdown
Contributor

Comparing benchmark results of 'validation-full' on '0468c1c57d' (base) and 'f2742c451c' (PR)

Results table
Script 0468c1c f2742c4 Change
auction_1-1 441.3 μs 428.4 μs -2.9%
auction_1-2 1.412 ms 1.404 ms -0.6%
auction_1-3 1.417 ms 1.402 ms -1.1%
auction_1-4 489.3 μs 480.3 μs -1.8%
auction_2-1 434.6 μs 427.8 μs -1.6%
auction_2-2 1.410 ms 1.401 ms -0.6%
auction_2-3 1.634 ms 1.619 ms -0.9%
auction_2-4 1.408 ms 1.396 ms -0.9%
auction_2-5 488.1 μs 479.0 μs -1.9%
coop-1 378.7 μs 382.2 μs +0.9%
coop-2 1.136 ms 1.122 ms -1.2%
coop-3 2.608 ms 2.598 ms -0.4%
coop-4 1.412 ms 1.401 ms -0.8%
coop-5 799.3 μs 789.5 μs -1.2%
coop-6 883.1 μs 898.5 μs +1.7%
coop-7 506.9 μs 503.6 μs -0.7%
crowdfunding-success-1 526.6 μs 515.8 μs -2.1%
crowdfunding-success-2 533.1 μs 515.6 μs -3.3%
crowdfunding-success-3 532.3 μs 516.3 μs -3.0%
currency-1 556.9 μs 547.0 μs -1.8%
escrow-redeem_1-1 776.7 μs 769.6 μs -0.9%
escrow-redeem_1-2 776.5 μs 770.3 μs -0.8%
escrow-redeem_2-1 833.1 μs 828.8 μs -0.5%
escrow-redeem_2-2 833.7 μs 828.9 μs -0.6%
escrow-redeem_2-3 833.7 μs 828.5 μs -0.6%
escrow-refund-1 578.4 μs 566.4 μs -2.1%
future-increase-margin-1 557.3 μs 545.2 μs -2.2%
future-increase-margin-2 967.8 μs 961.9 μs -0.6%
future-increase-margin-3 968.6 μs 962.9 μs -0.6%
future-increase-margin-4 1.447 ms 1.428 ms -1.3%
future-increase-margin-5 1.826 ms 1.802 ms -1.3%
future-pay-out-1 556.8 μs 545.0 μs -2.1%
future-pay-out-2 971.1 μs 962.1 μs -0.9%
future-pay-out-3 967.7 μs 961.6 μs -0.6%
future-pay-out-4 1.815 ms 1.806 ms -0.5%
future-settle-early-1 557.0 μs 544.8 μs -2.2%
future-settle-early-2 966.7 μs 962.1 μs -0.5%
future-settle-early-3 967.7 μs 961.1 μs -0.7%
future-settle-early-4 1.586 ms 1.569 ms -1.1%
game-sm-success_1-1 1.120 ms 1.095 ms -2.2%
game-sm-success_1-2 421.2 μs 410.5 μs -2.5%
game-sm-success_1-3 1.395 ms 1.374 ms -1.5%
game-sm-success_1-4 453.6 μs 439.0 μs -3.2%
game-sm-success_2-1 1.117 ms 1.094 ms -2.1%
game-sm-success_2-2 423.3 μs 410.2 μs -3.1%
game-sm-success_2-3 1.396 ms 1.374 ms -1.6%
game-sm-success_2-4 453.3 μs 439.5 μs -3.0%
game-sm-success_2-5 1.394 ms 1.376 ms -1.3%
game-sm-success_2-6 453.4 μs 440.0 μs -3.0%
guardrail-sorted-large 588.6 μs 578.1 μs -1.8%
guardrail-sorted-small 204.4 μs 199.7 μs -2.3%
guardrail-unsorted-large 744.1 μs 738.9 μs -0.7%
guardrail-unsorted-small 197.4 μs 193.6 μs -1.9%
multisig-sm-01 1.203 ms 1.190 ms -1.1%
multisig-sm-02 1.191 ms 1.180 ms -0.9%
multisig-sm-03 1.195 ms 1.182 ms -1.1%
multisig-sm-04 1.200 ms 1.189 ms -0.9%
multisig-sm-05 1.378 ms 1.369 ms -0.7%
multisig-sm-06 1.201 ms 1.189 ms -1.0%
multisig-sm-07 1.190 ms 1.180 ms -0.8%
multisig-sm-08 1.194 ms 1.181 ms -1.1%
multisig-sm-09 1.203 ms 1.188 ms -1.2%
multisig-sm-10 1.379 ms 1.368 ms -0.8%
ping-pong-1 1.076 ms 1.007 ms -6.4%
ping-pong-2 1.077 ms 1.007 ms -6.5%
ping-pong_2-1 856.6 μs 840.2 μs -1.9%
prism-1 380.6 μs 368.0 μs -3.3%
prism-2 1.129 ms 1.117 ms -1.1%
prism-3 684.2 μs 672.4 μs -1.7%
pubkey-1 360.3 μs 351.0 μs -2.6%
stablecoin_1-1 2.261 ms 2.244 ms -0.8%
stablecoin_1-2 415.6 μs 404.1 μs -2.8%
stablecoin_1-3 2.437 ms 2.413 ms -1.0%
stablecoin_1-4 429.4 μs 416.2 μs -3.1%
stablecoin_1-5 2.792 ms 2.771 ms -0.8%
stablecoin_1-6 477.5 μs 464.7 μs -2.7%
stablecoin_2-1 2.272 ms 2.243 ms -1.3%
stablecoin_2-2 415.1 μs 404.2 μs -2.6%
stablecoin_2-3 2.432 ms 2.414 ms -0.7%
stablecoin_2-4 428.9 μs 416.9 μs -2.8%
token-account-1 496.1 μs 486.2 μs -2.0%
token-account-2 616.2 μs 606.0 μs -1.7%
uniswap-1 711.9 μs 700.6 μs -1.6%
uniswap-2 530.8 μs 521.9 μs -1.7%
uniswap-3 2.951 ms 2.902 ms -1.7%
uniswap-4 571.7 μs 571.7 μs 0.0%
uniswap-5 2.235 ms 2.194 ms -1.8%
uniswap-6 556.8 μs 545.3 μs -2.1%
vesting-1 784.8 μs 774.6 μs -1.3%
0468c1c f2742c4 Change
TOTAL 89.87 ms 88.67 ms -1.3%

@zliu41
Copy link
Copy Markdown
Member

zliu41 commented Apr 28, 2026

I'll defer this to @kwxm

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

Labels

No Changelog Required Add this to skip the Changelog Check

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants