[Builtins] Add constant casing for builtin unit and pair#7221
[Builtins] Add constant casing for builtin unit and pair#7221SeungheonOh merged 15 commits intomasterfrom
Conversation
|
|
/benchmark lists |
|
Click here to check the status of your benchmark. |
|
Comparing benchmark results of 'lists' on '2a8dc3e39b' (base) and '77ec468270' (PR) Results table
|
|
I made changes to plugin so that it will use constant casing for |
| (fstPair {integer} {b} tup) | ||
| (sndPair {integer} {b} tup)) | ||
| (case integer tup [(\(l : integer) (r : b) -> l)]) | ||
| (case b tup [(\(l : integer) (r : b) -> r)])) |
There was a problem hiding this comment.
There's of course the issue that we want to only case on tup once, i.e. we currently generate sub-optimal code.
Not for this PR though, should be addressed in a follow-up.
| @@ -1 +1,2 @@ | |||
| \(p : pair integer integer) -> fstPair {integer} {integer} p No newline at end of file | |||
| \(p : pair integer integer) -> | |||
| case integer p [(\(l : integer) (r : integer) -> l)] No newline at end of file | |||
There was a problem hiding this comment.
Well, I guess it's not entirely unreasonable that calling a builtin once can be faster than evaluating a Case node, two function applications and a variable lookup.
There was a problem hiding this comment.
Especially given that a builtin does something very similar to what a case does, except with less noise and with better optimizations.
There was a problem hiding this comment.
Yeah, and cost model doesn't reflect this correctly. The cost might only even out when we compare taking both values from pair, running FstPair and SndPair both.
I don't think we can replace FstPair and SndPair
|
/benchmark lists |
|
Click here to check the status of your benchmark. |
|
Comparing benchmark results of 'lists' on '2a8dc3e39b' (base) and '77ec468270' (PR) Results table
|
|
Okay, we just got trolled by benchmark machine |
|
/benchmark casing |
|
Click here to check the status of your benchmark. |
Execution Budget Golden Diffoutputplutus-benchmark/cardano-loans/test/9.6/main.eval.golden
plutus-benchmark/coop/test/9.6/authMpBurning.eval.golden
plutus-benchmark/coop/test/9.6/authMpMinting.eval.golden
plutus-benchmark/coop/test/9.6/certMpBurning.eval.golden
plutus-benchmark/coop/test/9.6/certMpMinting.eval.golden
plutus-benchmark/coop/test/9.6/fsMpBurning.eval.golden
plutus-benchmark/coop/test/9.6/fsMpMinting.eval.golden
plutus-benchmark/coop/test/9.6/mustBurnOwnSingleton.eval.golden
plutus-benchmark/linear-vesting/test/9.6/main.eval.golden
plutus-benchmark/lists/test/Sum/9.6/left-fold-data.eval.golden
plutus-benchmark/lists/test/Sum/9.6/right-fold-data.eval.golden
plutus-benchmark/script-contexts/test/V1/9.6/checkScriptContext1-20.eval.golden
plutus-benchmark/script-contexts/test/V1/9.6/checkScriptContext1-4.eval.golden
plutus-benchmark/script-contexts/test/V1/9.6/checkScriptContext2-20.eval.golden
plutus-benchmark/script-contexts/test/V1/9.6/checkScriptContext2-4.eval.golden
plutus-benchmark/script-contexts/test/V1/Data/9.6/checkScriptContext1-20.eval.golden
plutus-benchmark/script-contexts/test/V1/Data/9.6/checkScriptContext1-4.eval.golden
plutus-benchmark/script-contexts/test/V2/9.6/checkScriptContext1-20.eval.golden
plutus-benchmark/script-contexts/test/V2/9.6/checkScriptContext1-4.eval.golden
plutus-benchmark/script-contexts/test/V2/9.6/checkScriptContext2-20.eval.golden
plutus-benchmark/script-contexts/test/V2/9.6/checkScriptContext2-4.eval.golden
plutus-benchmark/script-contexts/test/V2/9.6/dataFwdStakeTrick.eval.golden
plutus-benchmark/script-contexts/test/V2/9.6/dataFwdStakeTrickManual.eval.golden
plutus-benchmark/script-contexts/test/V2/9.6/sopFwdStakeTrick.eval.golden
plutus-benchmark/script-contexts/test/V2/Data/9.6/checkScriptContext1-20.eval.golden
plutus-benchmark/script-contexts/test/V2/Data/9.6/checkScriptContext1-4.eval.golden
plutus-benchmark/script-contexts/test/V3/9.6/checkScriptContext1-20.eval.golden
plutus-benchmark/script-contexts/test/V3/9.6/checkScriptContext1-4.eval.golden
plutus-benchmark/script-contexts/test/V3/9.6/checkScriptContext2-20.eval.golden
plutus-benchmark/script-contexts/test/V3/9.6/checkScriptContext2-4.eval.golden
plutus-benchmark/script-contexts/test/V3/Data/9.6/checkScriptContext1-20.eval.golden
plutus-benchmark/script-contexts/test/V3/Data/9.6/checkScriptContext1-4.eval.golden
plutus-benchmark/script-contexts/test/V3/Data/9.6/purposeIsWellFormed-4.eval.golden
plutus-ledger-api/test-plugin/Spec/Data/Budget/9.6/currencySymbolValueOf.eval.golden
plutus-ledger-api/test-plugin/Spec/Data/Budget/9.6/geq1.eval.golden
plutus-ledger-api/test-plugin/Spec/Data/Budget/9.6/geq2.eval.golden
plutus-ledger-api/test-plugin/Spec/Data/Budget/9.6/geq3.eval.golden
plutus-ledger-api/test-plugin/Spec/Data/Budget/9.6/geq4.eval.golden
plutus-ledger-api/test-plugin/Spec/Data/Budget/9.6/geq5.eval.golden
plutus-ledger-api/test-plugin/Spec/Data/Budget/9.6/gt1.eval.golden
plutus-ledger-api/test-plugin/Spec/Data/Budget/9.6/gt2.eval.golden
plutus-ledger-api/test-plugin/Spec/Data/Budget/9.6/gt3.eval.golden
plutus-ledger-api/test-plugin/Spec/Data/Budget/9.6/gt4.eval.golden
plutus-ledger-api/test-plugin/Spec/Data/Budget/9.6/gt5.eval.golden
plutus-ledger-api/test-plugin/Spec/Data/Budget/9.6/mintValueBurned.eval.golden
plutus-ledger-api/test-plugin/Spec/Data/Budget/9.6/mintValueMinted.eval.golden
plutus-tx-plugin/test/AsData/Budget/9.6/destructSum-manual.eval.golden
plutus-tx-plugin/test/AsData/Budget/9.6/destructSum.eval.golden
plutus-tx-plugin/test/AsData/Budget/9.6/onlyUseFirstField-manual.eval.golden
plutus-tx-plugin/test/AsData/Budget/9.6/onlyUseFirstField.eval.golden
plutus-tx-plugin/test/AsData/Budget/9.6/patternMatching.eval.golden
plutus-tx-plugin/test/AsData/Budget/9.6/recordFields.eval.golden
plutus-tx-plugin/test/Budget/9.6/map1.eval.golden
plutus-tx-plugin/test/Budget/9.6/map2.eval.golden
plutus-tx-plugin/test/Budget/9.6/map3.eval.golden
plutus-tx-plugin/test/Budget/9.6/matchAsDataE.eval.golden
plutus-tx-plugin/test/Budget/9.6/toFromData.eval.golden
This comment will get updated when changes are made. |
Ran benchmark again locally, this compares implementation of |
| case | ||
| (equalsInteger | ||
| 5 | ||
| (case | ||
| tup |
There was a problem hiding this comment.
Interesting, so case-of-case can be generalized such that it transforms, say,
case (nullList (case tup (\x y -> x : xs)))
falseAlt
trueAlt
into
case tup
\x y ->
case nullList (x : xs)
falseAlt
trueAlt
which then becomes
case tup
\x y -> falseAlt
This wouldn't help here, but may or may not be a useful optimization in general.
Actually, we don't even need to care about the outer caseList, we just need to flip nullList and the inner case. Which is kinda what we do for ifThenElse. So yeah, it makes sense nullList is itself a glorified case.
And it works the other way too: case-of-nullList is as interesting to optimize as nullList-of-case. Obviously applies to other builtins like headList and fstPair etc. We do already handle ifThenElse (even though we don't actually generate it anymore), but only in one direction.
There was a problem hiding this comment.
it makes sense nullList is itself a glorified case
Yeah, It can be literally just
case <list>
(\_ _ -> falseCase)
trueCase
Which I'm not entirely sure if it will be faster than builtin. I'll put this on my list to benchmark. Although it would be easier to keep nullList for optimization, I think.
I'm not entirely sure if this will improve anything drastically. IMO, we should wait until we figure out if builtin nullList is faster than casing nullList and proceed accordingly.
cardano-constitution/test/Cardano/Constitution/Validator/GoldenTests/unsorted.uplc.golden
Outdated
Show resolved
Hide resolved
| (case | ||
| tup | ||
| [ (\l | ||
| r -> | ||
| r) ])) | ||
| (case | ||
| tup | ||
| [ (\l | ||
| r -> | ||
| l) ])) |
There was a problem hiding this comment.
I feel like we should cache these two somehow? They appear quite a few times in the file.
plutus-benchmark/script-contexts/test/V1/9.6/checkScriptContext1.pir.golden
Outdated
Show resolved
Hide resolved
| @@ -568,6 +577,11 @@ instance CaseBuiltin DefaultUni where | |||
| [] -> Right $ HeadOnly $ branches Vector.! 1 | |||
| (y : ys) -> Right $ headSpine (branches Vector.! 0) [someValueOf ty y, someValueOf uni ys] | |||
| | otherwise -> Left $ outOfBoundsErr someVal branches | |||
There was a problem hiding this comment.
Hm, I thought we've discussed it, but perhaps it was something else. We should have a CaseResult data type that is properly strict and removes the Either indirection that we have here.
It'll mean adding a constructor for errors to HeadSpine, since we only use that for casing on constants nowadays anyway.
There was a problem hiding this comment.
Will make a separate optimization PR
a06f3f0 to
c49099d
Compare
* Add constant casing for unit and pair, make builtin use constant casing * Update goldens * changelog * Add benchmark for comparing Pair/Unit builtins to casing * Add headList builtin vs casing bench * comments * Add conformance test for pair/unit casing * casing unit should only allow single branch * tidy * Add `casePair` and make `fromOpaque` use it * Make `FromData` use case pair * blacklist pair/unit casing conformance on agda
part of #6602
closes #7220
Casing on unit expects exactly one branch with no arguments, which will get picked everytime.
Casing on pair also expects exactly one branch with two arguments, for each parts of the pair.