UPLC optimization pass: hoist polymorphic builtins#7779
Conversation
11007c5 to
ad3e20b
Compare
|
ad3e20b to
8e60135
Compare
SeungheonOh
left a comment
There was a problem hiding this comment.
adjusting optimization step could yield better result. Currently, BuiltinPoly runs at the very end, so hoisted builtin bindings won't get turned into more optimal Case/Constr. Also, inliner won't run on hoisted builtins that were used only once, which is the reason why there are some performance regressions.
This is how it runs
simplifyNTimes (_ooMaxSimplifierIterations opts)
>=> runStage CseStage
>=> runStage ApplyToCaseStage
>=> runStage PolyBuiltinStage
would be better to do
runStage PolyBuiltinStage
>=> simplifyNTimes (_ooMaxSimplifierIterations opts)
>=> runStage CseStage
>=> runStage ApplyToCaseStage
| @@ -1 +1 @@ | |||
| (program 1.1.0 (\xs -> force mkCons 0 xs)) | |||
There was a problem hiding this comment.
Hoisting builtin that was used once(or even twice) is worse for performance. I don't think it would be too difficult to check how many times it was used.
Also, this might not even need to be manually handled if we run inline pass after builtin poly at least for single-use cases.
| where | ||
| (n, t') = peelForces t | ||
|
|
||
| candidates :: HashMap fun Int |
There was a problem hiding this comment.
I don't think it's useful to keep track of number of forces applied here since we already know that from builtin itself and we are not hoisting any partially instantiated builtins.
However, I think the Int here can be used to track occurrences so that only builtins that were used more than twice gets hoisted. This way we don't have to rely on inliner to do this.
There was a problem hiding this comment.
This Int just keeps track of how many forces a builtin need, so that applyForces applies the right number of forces. We can call builtinArity in applyForces again, but that's more costly.
|
I was wrong, we don't want to inline soley based on occurrence count, since it could be inlined into places that will be run multiple times. This would need more complicated fix-point detection to prevent inlining accross fixpoint combinator which we don't have now. |
zliu41
left a comment
There was a problem hiding this comment.
The idea is to hoist forced builtins blindly, like what Plutarch does.
It is run at the end of the pipeline to avoid the inliner inlining let forcedFstPair = force (force fstPair) in List.any (\x -> ...forcedFstPair...) xs.
| where | ||
| (n, t') = peelForces t | ||
|
|
||
| candidates :: HashMap fun Int |
There was a problem hiding this comment.
This Int just keeps track of how many forces a builtin need, so that applyForces applies the right number of forces. We can call builtinArity in applyForces again, but that's more costly.
Execution Budget Golden Diffoutputplutus-benchmark/bitwise/test/9.6/8
plutus-benchmark/bitwise/test/9.6/Ed25519.golden.eval
plutus-benchmark/cardano-loans/test/9.6/main.golden.eval
plutus-benchmark/coop/test/9.6/authMpBurning.golden.eval
plutus-benchmark/coop/test/9.6/authMpMinting.golden.eval
plutus-benchmark/coop/test/9.6/certMpBurning.golden.eval
plutus-benchmark/coop/test/9.6/certMpMinting.golden.eval
plutus-benchmark/coop/test/9.6/fsMpBurning.golden.eval
plutus-benchmark/coop/test/9.6/fsMpMinting.golden.eval
plutus-benchmark/coop/test/9.6/mustBurnOwnSingleton.golden.eval
plutus-benchmark/linear-vesting/test/9.6/main.golden.eval
plutus-benchmark/lists/test/Lookup/9.6/match-builtin-list-10.golden.eval
plutus-benchmark/lists/test/Lookup/9.6/match-builtin-list-100.golden.eval
plutus-benchmark/lists/test/Lookup/9.6/match-builtin-list-5.golden.eval
plutus-benchmark/lists/test/Lookup/9.6/match-builtin-list-50.golden.eval
plutus-benchmark/lists/test/Lookup/9.6/match-scott-list-10.golden.eval
plutus-benchmark/lists/test/Lookup/9.6/match-scott-list-100.golden.eval
plutus-benchmark/lists/test/Lookup/9.6/match-scott-list-5.golden.eval
plutus-benchmark/lists/test/Lookup/9.6/match-scott-list-50.golden.eval
plutus-benchmark/lists/test/Sum/9.6/left-fold-data.golden.eval
plutus-benchmark/lists/test/Sum/9.6/right-fold-data.golden.eval
plutus-benchmark/nofib/test/9.6/clausify-F5.golden.eval
plutus-benchmark/nofib/test/9.6/knights10-4x4.golden.eval
plutus-benchmark/nofib/test/9.6/queens4-bt.golden.eval
plutus-benchmark/nofib/test/9.6/queens5-fc.golden.eval
plutus-benchmark/script-contexts/test/V1/9.6/checkScriptContext1-20.golden.eval
plutus-benchmark/script-contexts/test/V1/9.6/checkScriptContext1-4.golden.eval
plutus-benchmark/script-contexts/test/V1/9.6/checkScriptContext2-20.golden.eval
plutus-benchmark/script-contexts/test/V1/9.6/checkScriptContext2-4.golden.eval
plutus-benchmark/script-contexts/test/V1/9.6/checkScriptContextEqualityData-20.golden.eval
plutus-benchmark/script-contexts/test/V1/Data/9.6/checkScriptContext1-20.golden.eval
plutus-benchmark/script-contexts/test/V1/Data/9.6/checkScriptContext1-4.golden.eval
plutus-benchmark/script-contexts/test/V1/Data/9.6/checkScriptContextEqualityData-20.golden.eval
plutus-benchmark/script-contexts/test/V2/9.6/checkScriptContext1-20.golden.eval
plutus-benchmark/script-contexts/test/V2/9.6/checkScriptContext1-4.golden.eval
plutus-benchmark/script-contexts/test/V2/9.6/checkScriptContext2-20.golden.eval
plutus-benchmark/script-contexts/test/V2/9.6/checkScriptContext2-4.golden.eval
plutus-benchmark/script-contexts/test/V2/9.6/checkScriptContextEqualityData-20.golden.eval
plutus-benchmark/script-contexts/test/V2/9.6/dataFwdStakeTrick.golden.eval
plutus-benchmark/script-contexts/test/V2/9.6/dataFwdStakeTrickManual.golden.eval
plutus-benchmark/script-contexts/test/V2/9.6/sopFwdStakeTrick.golden.eval
plutus-benchmark/script-contexts/test/V2/Data/9.6/checkScriptContext1-20.golden.eval
plutus-benchmark/script-contexts/test/V2/Data/9.6/checkScriptContext1-4.golden.eval
plutus-benchmark/script-contexts/test/V2/Data/9.6/checkScriptContextEqualityData-20.golden.eval
plutus-benchmark/script-contexts/test/V3/9.6/checkScriptContext1-20.golden.eval
plutus-benchmark/script-contexts/test/V3/9.6/checkScriptContext1-4.golden.eval
plutus-benchmark/script-contexts/test/V3/9.6/checkScriptContext2-20.golden.eval
plutus-benchmark/script-contexts/test/V3/9.6/checkScriptContext2-4.golden.eval
plutus-benchmark/script-contexts/test/V3/9.6/checkScriptContextEqualityData-20.golden.eval
plutus-benchmark/script-contexts/test/V3/Data/9.6/checkScriptContext1-20.golden.eval
plutus-benchmark/script-contexts/test/V3/Data/9.6/checkScriptContext1-4.golden.eval
plutus-benchmark/script-contexts/test/V3/Data/9.6/checkScriptContextEqualityData-20.golden.eval
plutus-benchmark/script-contexts/test/V3/Data/9.6/purposeIsWellFormed-4.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.12/geq1.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.12/geq2.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.12/geq3.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.12/geq4.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.12/geq5.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.12/gt1.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.12/gt2.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.12/gt3.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.12/gt4.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.12/gt5.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.6/geq1.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.6/geq2.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.6/geq3.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.6/geq4.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.6/geq5.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.6/gt1.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.6/gt2.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.6/gt3.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.6/gt4.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Budget/9.6/gt5.golden.eval
plutus-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.12/mintValueBurned.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/mintValueMinted.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
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.6/mintValueBurned.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.6/mintValueMinted.golden.eval
plutus-tx-plugin/test/AsData/Budget/9.12/destructSum-manual.golden.eval
plutus-tx-plugin/test/AsData/Budget/9.12/destructSum.golden.eval
plutus-tx-plugin/test/AsData/Budget/9.12/onlyUseFirstField-manual.golden.eval
plutus-tx-plugin/test/AsData/Budget/9.12/onlyUseFirstField.golden.eval
plutus-tx-plugin/test/AsData/Budget/9.12/patternMatching.golden.eval
plutus-tx-plugin/test/AsData/Budget/9.12/recordFields.golden.eval
plutus-tx-plugin/test/AsData/Budget/9.6/destructSum-manual.golden.eval
plutus-tx-plugin/test/AsData/Budget/9.6/destructSum.golden.eval
plutus-tx-plugin/test/AsData/Budget/9.6/onlyUseFirstField-manual.golden.eval
plutus-tx-plugin/test/AsData/Budget/9.6/onlyUseFirstField.golden.eval
plutus-tx-plugin/test/AsData/Budget/9.6/patternMatching.golden.eval
plutus-tx-plugin/test/AsData/Budget/9.6/recordFields.golden.eval
plutus-tx-plugin/test/Budget/9.12/allCheap.golden.eval
plutus-tx-plugin/test/Budget/9.12/allExpensive.golden.eval
plutus-tx-plugin/test/Budget/9.12/anyCheap.golden.eval
plutus-tx-plugin/test/Budget/9.12/anyExpensive.golden.eval
plutus-tx-plugin/test/Budget/9.12/findCheap.golden.eval
plutus-tx-plugin/test/Budget/9.12/findExpensive.golden.eval
plutus-tx-plugin/test/Budget/9.12/findIndexCheap.golden.eval
plutus-tx-plugin/test/Budget/9.12/findIndexExpensive.golden.eval
plutus-tx-plugin/test/Budget/9.12/gte0.golden.eval
plutus-tx-plugin/test/Budget/9.12/listIndexing.golden.eval
plutus-tx-plugin/test/Budget/9.12/map1.golden.eval
plutus-tx-plugin/test/Budget/9.12/map2.golden.eval
plutus-tx-plugin/test/Budget/9.12/map3.golden.eval
plutus-tx-plugin/test/Budget/9.12/matchAsDataE.golden.eval
plutus-tx-plugin/test/Budget/9.12/recursiveGte0.golden.eval
plutus-tx-plugin/test/Budget/9.12/show.golden.eval
plutus-tx-plugin/test/Budget/9.12/sumAtIndices.golden.eval
plutus-tx-plugin/test/Budget/9.12/sumL.golden.eval
plutus-tx-plugin/test/Budget/9.12/sumR.golden.eval
plutus-tx-plugin/test/Budget/9.6/allCheap.golden.eval
plutus-tx-plugin/test/Budget/9.6/allExpensive.golden.eval
plutus-tx-plugin/test/Budget/9.6/anyCheap.golden.eval
plutus-tx-plugin/test/Budget/9.6/anyExpensive.golden.eval
plutus-tx-plugin/test/Budget/9.6/findCheap.golden.eval
plutus-tx-plugin/test/Budget/9.6/findExpensive.golden.eval
plutus-tx-plugin/test/Budget/9.6/findIndexCheap.golden.eval
plutus-tx-plugin/test/Budget/9.6/findIndexExpensive.golden.eval
plutus-tx-plugin/test/Budget/9.6/gte0.golden.eval
plutus-tx-plugin/test/Budget/9.6/listIndexing.golden.eval
plutus-tx-plugin/test/Budget/9.6/map1.golden.eval
plutus-tx-plugin/test/Budget/9.6/map2.golden.eval
plutus-tx-plugin/test/Budget/9.6/map3.golden.eval
plutus-tx-plugin/test/Budget/9.6/matchAsDataE.golden.eval
plutus-tx-plugin/test/Budget/9.6/recursiveGte0.golden.eval
plutus-tx-plugin/test/Budget/9.6/show.golden.eval
plutus-tx-plugin/test/Budget/9.6/sumAtIndices.golden.eval
plutus-tx-plugin/test/Budget/9.6/sumL.golden.eval
plutus-tx-plugin/test/Budget/9.6/sumR.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/all.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/any.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/append.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/concat.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/concatMap.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/concat_operator.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/cons.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/cons_operator.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/filter.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/find.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/findIndices.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/headOk.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/index.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/lastOk.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/map.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/mapMaybe.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/nub.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/nubBy.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/null.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/replicate.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/revAppend.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/reverse.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/tailOk.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/take.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/uniqueElementJust.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.12/zipWith.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/all.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/any.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/append.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/concat.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/concatMap.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/concat_operator.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/cons.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/cons_operator.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/filter.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/find.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/findIndices.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/headOk.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/index.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/lastOk.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/map.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/mapMaybe.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/nub.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/nubBy.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/null.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/replicate.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/revAppend.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/reverse.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/tailOk.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/take.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/uniqueElementJust.golden.eval
plutus-tx-plugin/test/BuiltinList/Budget/9.6/zipWith.golden.eval
plutus-tx-plugin/test/BuiltinList/NoCasing/9.12/unsafeUnconsOk.golden.eval
plutus-tx-plugin/test/BuiltinList/NoCasing/9.6/unsafeUnconsOk.golden.eval
plutus-tx-plugin/test/CallTrace/9.12/successfullEvaluationYieldsNoTraceLog.golden.eval
plutus-tx-plugin/test/CallTrace/9.6/successfullEvaluationYieldsNoTraceLog.golden.eval
plutus-tx-plugin/test/DataList/Budget/9.12/any.golden.eval
plutus-tx-plugin/test/DataList/Budget/9.12/filter.golden.eval
plutus-tx-plugin/test/DataList/Budget/9.12/makeList.golden.eval
plutus-tx-plugin/test/DataList/Budget/9.12/partition.golden.eval
plutus-tx-plugin/test/DataList/Budget/9.6/any.golden.eval
plutus-tx-plugin/test/DataList/Budget/9.6/filter.golden.eval
plutus-tx-plugin/test/DataList/Budget/9.6/makeList.golden.eval
plutus-tx-plugin/test/DataList/Budget/9.6/partition.golden.eval
plutus-tx-plugin/test/IsData/Budget/BuiltinCasing/9.12/decodeA.golden.eval
plutus-tx-plugin/test/IsData/Budget/BuiltinCasing/9.12/decodeC.golden.eval
plutus-tx-plugin/test/IsData/Budget/BuiltinCasing/9.12/decodePairA.golden.eval
plutus-tx-plugin/test/IsData/Budget/BuiltinCasing/9.12/decodePairB.golden.eval
plutus-tx-plugin/test/IsData/Budget/BuiltinCasing/9.12/decodeSingle.golden.eval
plutus-tx-plugin/test/IsData/Budget/BuiltinCasing/9.6/decodeA.golden.eval
plutus-tx-plugin/test/IsData/Budget/BuiltinCasing/9.6/decodeC.golden.eval
plutus-tx-plugin/test/IsData/Budget/BuiltinCasing/9.6/decodePairA.golden.eval
plutus-tx-plugin/test/IsData/Budget/BuiltinCasing/9.6/decodePairB.golden.eval
plutus-tx-plugin/test/IsData/Budget/BuiltinCasing/9.6/decodeSingle.golden.eval
plutus-tx-plugin/test/IsData/Budget/SoP/9.12/decodeA.golden.eval
plutus-tx-plugin/test/IsData/Budget/SoP/9.12/decodeC.golden.eval
plutus-tx-plugin/test/IsData/Budget/SoP/9.12/decodeMixedNone.golden.eval
plutus-tx-plugin/test/IsData/Budget/SoP/9.12/decodeMixedTwo.golden.eval
plutus-tx-plugin/test/IsData/Budget/SoP/9.12/decodePairA.golden.eval
plutus-tx-plugin/test/IsData/Budget/SoP/9.12/decodePairB.golden.eval
plutus-tx-plugin/test/IsData/Budget/SoP/9.12/decodeSingle.golden.eval
plutus-tx-plugin/test/IsData/Budget/SoP/9.6/decodeA.golden.eval
plutus-tx-plugin/test/IsData/Budget/SoP/9.6/decodeC.golden.eval
plutus-tx-plugin/test/IsData/Budget/SoP/9.6/decodeMixedNone.golden.eval
plutus-tx-plugin/test/IsData/Budget/SoP/9.6/decodeMixedTwo.golden.eval
plutus-tx-plugin/test/IsData/Budget/SoP/9.6/decodePairA.golden.eval
plutus-tx-plugin/test/IsData/Budget/SoP/9.6/decodePairB.golden.eval
plutus-tx-plugin/test/IsData/Budget/SoP/9.6/decodeSingle.golden.eval
This comment will get updated when changes are made. |
Before:
...force (force fstPair)...After:
(\fstPairForced -> ...fstPairForced...) (force (force fstPair))