Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Builtins] Optimize costing by being lazier #5239

Merged
merged 26 commits into from
Apr 16, 2023

Conversation

effectfully
Copy link
Contributor

@effectfully effectfully commented Mar 28, 2023

This makes costing lazier to improve both performance and expressiveness.

The Costing.hs module with tests isn't ready for review yet, everything else is ready for review.

@effectfully
Copy link
Contributor Author

/benchmark validation

@github-actions
Copy link
Contributor

Click here to check the status of your benchmark.

@github-actions
Copy link
Contributor

Comparing benchmark results of ' validation' on '411cf29bfb' (base) and '7fa2872e5d' (PR)

Results table
Script 411cf29 7fa2872 Change
auction_1-1 166.3 μs 166.8 μs +0.3%
auction_1-2 678.3 μs 691.6 μs +2.0%
auction_1-3 668.8 μs 686.4 μs +2.6%
auction_1-4 219.1 μs 217.2 μs -0.9%
auction_2-1 169.3 μs 168.3 μs -0.6%
auction_2-2 677.8 μs 686.7 μs +1.3%
auction_2-3 889.8 μs 896.6 μs +0.8%
auction_2-4 671.1 μs 675.3 μs +0.6%
auction_2-5 219.4 μs 217.6 μs -0.8%
crowdfunding-success-1 199.1 μs 196.6 μs -1.3%
crowdfunding-success-2 199.2 μs 195.3 μs -2.0%
crowdfunding-success-3 199.8 μs 195.2 μs -2.3%
currency-1 251.7 μs 252.3 μs +0.2%
escrow-redeem_1-1 347.9 μs 354.2 μs +1.8%
escrow-redeem_1-2 348.2 μs 353.3 μs +1.5%
escrow-redeem_2-1 413.8 μs 412.2 μs -0.4%
escrow-redeem_2-2 411.7 μs 412.4 μs +0.2%
escrow-redeem_2-3 411.0 μs 412.7 μs +0.4%
escrow-refund-1 147.3 μs 146.8 μs -0.3%
future-increase-margin-1 250.6 μs 251.0 μs +0.2%
future-increase-margin-2 550.4 μs 548.6 μs -0.3%
future-increase-margin-3 548.0 μs 549.3 μs +0.2%
future-increase-margin-4 511.0 μs 518.9 μs +1.5%
future-increase-margin-5 871.9 μs 884.0 μs +1.4%
future-pay-out-1 251.0 μs 251.3 μs +0.1%
future-pay-out-2 548.2 μs 548.8 μs +0.1%
future-pay-out-3 550.4 μs 549.8 μs -0.1%
future-pay-out-4 867.8 μs 885.4 μs +2.0%
future-settle-early-1 250.7 μs 250.3 μs -0.2%
future-settle-early-2 547.0 μs 544.0 μs -0.5%
future-settle-early-3 547.3 μs 543.7 μs -0.7%
future-settle-early-4 647.9 μs 647.7 μs -0.0%
game-sm-success_1-1 397.2 μs 394.6 μs -0.7%
game-sm-success_1-2 187.9 μs 183.9 μs -2.1%
game-sm-success_1-3 668.8 μs 660.6 μs -1.2%
game-sm-success_1-4 217.4 μs 212.9 μs -2.1%
game-sm-success_2-1 397.6 μs 394.1 μs -0.9%
game-sm-success_2-2 187.7 μs 183.1 μs -2.5%
game-sm-success_2-3 668.0 μs 661.0 μs -1.0%
game-sm-success_2-4 217.3 μs 212.0 μs -2.4%
game-sm-success_2-5 670.6 μs 661.6 μs -1.3%
game-sm-success_2-6 217.9 μs 213.0 μs -2.2%
multisig-sm-1 407.2 μs 413.5 μs +1.5%
multisig-sm-2 404.1 μs 401.0 μs -0.8%
multisig-sm-3 408.2 μs 408.6 μs +0.1%
multisig-sm-4 414.6 μs 413.5 μs -0.3%
multisig-sm-5 589.7 μs 589.9 μs +0.0%
multisig-sm-6 406.7 μs 411.2 μs +1.1%
multisig-sm-7 405.6 μs 403.7 μs -0.5%
multisig-sm-8 409.6 μs 407.4 μs -0.5%
multisig-sm-9 412.4 μs 413.1 μs +0.2%
multisig-sm-10 593.1 μs 590.0 μs -0.5%
ping-pong-1 338.4 μs 337.4 μs -0.3%
ping-pong-2 340.3 μs 338.1 μs -0.6%
ping-pong_2-1 200.7 μs 196.2 μs -2.2%
prism-1 155.7 μs 153.4 μs -1.5%
prism-2 423.6 μs 421.3 μs -0.5%
prism-3 362.9 μs 366.7 μs +1.0%
pubkey-1 133.1 μs 128.9 μs -3.2%
stablecoin_1-1 970.6 μs 979.3 μs +0.9%
stablecoin_1-2 183.7 μs 179.5 μs -2.3%
stablecoin_1-3 1.113 ms 1.123 ms +0.9%
stablecoin_1-4 193.7 μs 190.1 μs -1.9%
stablecoin_1-5 1.430 ms 1.423 ms -0.5%
stablecoin_1-6 240.4 μs 233.4 μs -2.9%
stablecoin_2-1 969.5 μs 981.7 μs +1.3%
stablecoin_2-2 182.7 μs 179.5 μs -1.8%
stablecoin_2-3 1.121 ms 1.125 ms +0.4%
stablecoin_2-4 194.8 μs 191.1 μs -1.9%
token-account-1 184.4 μs 182.5 μs -1.0%
token-account-2 332.3 μs 331.2 μs -0.3%
uniswap-1 421.3 μs 420.0 μs -0.3%
uniswap-2 213.3 μs 216.4 μs +1.5%
uniswap-3 1.864 ms 1.863 ms -0.1%
uniswap-4 312.4 μs 311.3 μs -0.4%
uniswap-5 1.196 ms 1.194 ms -0.2%
uniswap-6 301.4 μs 298.9 μs -0.8%
vesting-1 357.6 μs 360.4 μs +0.8%

@effectfully
Copy link
Contributor Author

/benchmark nofib

@effectfully
Copy link
Contributor Author

/benchmark benchmark:nofib

@effectfully
Copy link
Contributor Author

Eek.

@effectfully
Copy link
Contributor Author

/benchmark plutus-benchmark:nofib

@github-actions
Copy link
Contributor

Click here to check the status of your benchmark.

@github-actions
Copy link
Contributor

Comparing benchmark results of ' nofib' on '6f3f472138' (base) and '7fa2872e5d' (PR)

Results table
Script 6f3f472 7fa2872 Change
clausify/formula1 13.97 ms 13.90 ms -0.5%
clausify/formula2 18.07 ms 17.83 ms -1.3%
clausify/formula3 49.55 ms 49.08 ms -0.9%
clausify/formula4 75.62 ms 75.51 ms -0.1%
clausify/formula5 308.7 ms 304.4 ms -1.4%
knights/4x4 54.00 ms 54.76 ms +1.4%
knights/6x6 144.3 ms 145.0 ms +0.5%
knights/8x8 237.6 ms 238.3 ms +0.3%
primetest/05digits 27.13 ms 26.51 ms -2.3%
primetest/08digits 49.26 ms 48.42 ms -1.7%
primetest/10digits 68.85 ms 67.60 ms -1.8%
primetest/20digits 143.9 ms 140.3 ms -2.5%
primetest/30digits 212.1 ms 206.8 ms -2.5%
primetest/40digits 292.7 ms 285.7 ms -2.4%
primetest/50digits 277.9 ms 272.8 ms -1.8%
queens4x4/bt 9.719 ms 9.637 ms -0.8%
queens4x4/bm 13.54 ms 13.44 ms -0.7%
queens4x4/bjbt1 11.99 ms 11.88 ms -0.9%
queens4x4/bjbt2 12.08 ms 12.11 ms +0.2%
queens4x4/fc 25.59 ms 25.55 ms -0.2%
queens5x5/bt 127.6 ms 127.5 ms -0.1%
queens5x5/bm 159.8 ms 157.1 ms -1.7%
queens5x5/bjbt1 152.0 ms 152.5 ms +0.3%
queens5x5/bjbt2 154.3 ms 157.3 ms +1.9%
queens5x5/fc 324.3 ms 325.2 ms +0.3%

@github-actions
Copy link
Contributor

Click here to check the status of your benchmark.

@github-actions
Copy link
Contributor

Comparing benchmark results of ' benchmark:nofib' on '6f3f472138' (base) and '7fa2872e5d' (PR)

Results table
Script 6f3f472 7fa2872 Change
clausify/formula1 13.95 ms 13.93 ms -0.1%
clausify/formula2 18.01 ms 17.84 ms -0.9%
clausify/formula3 49.42 ms 49.07 ms -0.7%
clausify/formula4 75.57 ms 74.99 ms -0.8%
clausify/formula5 308.0 ms 303.9 ms -1.3%
knights/4x4 54.12 ms 55.18 ms +2.0%
knights/6x6 144.4 ms 145.2 ms +0.6%
knights/8x8 238.2 ms 239.5 ms +0.5%
primetest/05digits 27.15 ms 26.67 ms -1.8%
primetest/08digits 49.59 ms 48.48 ms -2.2%
primetest/10digits 68.85 ms 67.70 ms -1.7%
primetest/20digits 143.8 ms 140.9 ms -2.0%
primetest/30digits 211.7 ms 208.0 ms -1.7%
primetest/40digits 291.1 ms 286.1 ms -1.7%
primetest/50digits 277.4 ms 273.5 ms -1.4%
queens4x4/bt 9.726 ms 9.701 ms -0.3%
queens4x4/bm 13.62 ms 13.54 ms -0.6%
queens4x4/bjbt1 11.87 ms 12.09 ms +1.9%
queens4x4/bjbt2 12.09 ms 12.25 ms +1.3%
queens4x4/fc 25.42 ms 25.47 ms +0.2%
queens5x5/bt 126.0 ms 126.5 ms +0.4%
queens5x5/bm 158.7 ms 155.3 ms -2.1%
queens5x5/bjbt1 151.1 ms 152.1 ms +0.7%
queens5x5/bjbt2 154.1 ms 154.7 ms +0.4%
queens5x5/fc 323.8 ms 322.7 ms -0.3%

@github-actions
Copy link
Contributor

Click here to check the status of your benchmark.

@github-actions
Copy link
Contributor

Comparing benchmark results of ' plutus-benchmark:nofib' on '6f3f472138' (base) and '7fa2872e5d' (PR)

Results table
Script 6f3f472 7fa2872 Change
clausify/formula1 14.01 ms 13.91 ms -0.7%
clausify/formula2 18.05 ms 17.97 ms -0.4%
clausify/formula3 49.93 ms 49.35 ms -1.2%
clausify/formula4 76.93 ms 75.56 ms -1.8%
clausify/formula5 310.7 ms 305.6 ms -1.6%
knights/4x4 54.08 ms 54.83 ms +1.4%
knights/6x6 145.7 ms 146.0 ms +0.2%
knights/8x8 239.6 ms 239.5 ms -0.0%
primetest/05digits 27.17 ms 26.59 ms -2.1%
primetest/08digits 49.34 ms 48.53 ms -1.6%
primetest/10digits 68.87 ms 67.78 ms -1.6%
primetest/20digits 144.0 ms 141.2 ms -1.9%
primetest/30digits 212.1 ms 208.0 ms -1.9%
primetest/40digits 293.2 ms 287.3 ms -2.0%
primetest/50digits 279.2 ms 274.9 ms -1.5%
queens4x4/bt 9.827 ms 9.880 ms +0.5%
queens4x4/bm 13.81 ms 13.85 ms +0.3%
queens4x4/bjbt1 12.04 ms 12.28 ms +2.0%
queens4x4/bjbt2 12.29 ms 12.54 ms +2.0%
queens4x4/fc 26.04 ms 26.13 ms +0.3%
queens5x5/bt 127.8 ms 128.3 ms +0.4%
queens5x5/bm 160.4 ms 158.7 ms -1.1%
queens5x5/bjbt1 154.3 ms 158.5 ms +2.7%
queens5x5/bjbt2 156.4 ms 158.3 ms +1.2%
queens5x5/fc 325.2 ms 327.3 ms +0.6%

CostCons (coerce $ I# i0) $
case coerce cost1 of
I# i1 ->
flattenCostRoseGo i1 forest1 $ \i2 ->
Copy link
Member

Choose a reason for hiding this comment

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

As far as I can tell this is amortized O(1) for producing each element, but could take linear time for some elements (e.g., the last one). Is this optimal?

Copy link
Contributor Author

@effectfully effectfully Mar 28, 2023

Choose a reason for hiding this comment

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

Hm, I'm not seeing any linear time in there, could you explain? Linear memory in the depth of the tree -- sure, but I don't think we can do better than that and it's conservative too (it's just that previously the linearity-of-memory-consumption was in the ExMemoryUsage Data instance rather than here).

So flattenCostRoseGo always immediately emits an element when the forest is non-empty. Then it grows the continuation (constant-time too) and recurses into the new node. If the new node is non-empty again, then we get another element in constant time, grow the continuation in constant time and recurse again. If the next node contains an empty forest, then we don't emit an element and jump to the continuation, which in constant time reduces to a flattenCostRoseGo call (i.e. without forcing the entirety of the so-far-accumulated continuation, just by reducing the outermost lambda), which again emits a cost immediately.

Thus, it seems to me that every cost is emitted in constant time, but maybe I'm misunderstanding something?

Copy link
Member

Choose a reason for hiding this comment

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

flattenCostRose (CostRose 1 [r1]) where
  r1 = CostRose 2 [r2]
  r2 = CostRose 3 [r3]
  r3 = CostRose 4 []

In this case it will reduce to

CostCons 1 $ CostCons 2 $ CostCons 3 $ <continuation that produces CostLast 4 in 4 steps>

The continuation is essentially

\x1 -> flattenCostRoseGo x1 [] $
  \x2 -> flattenCostRoseGo x2 [] $
    \x3 -> flattenCostRoseGo x3 [] $
      \x4 -> CostLast x4

So it seems producing 4 takes linear time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK, I was indeed misunderstanding, thanks a lot for the clarification and for catching. I'll think if it's a problem (releasing a linear amount of memory is a linear operation anyway, so maybe we can wait for the final cost as well, but not sure) and what to do about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So it seems producing 4 takes linear time.

(linear in the depth of the tree, just like memory consumption)

Copy link
Member

Choose a reason for hiding this comment

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

If it's amortized O(1) then it's not a problem. I think the following is O(1) worst case, but I wouldn't be surprised if it's slower:

flattenCostRose :: CostRose -> CostStream
flattenCostRose (CostRose cost []) = CostLast cost
flattenCostRose (CostRose cost (r:rs)) = CostCons cost (go r rs)

go :: CostRose -> [CostRose] -> CostStream
go (CostRose cost []) [] = CostLast cost
go (CostRose cost []) (r:rs) = CostCons cost (go r rs)
go (CostRose cost (r:rs)) rs' = CostCons cost (go r (rs ++ rs'))

Copy link
Contributor Author

@effectfully effectfully Mar 29, 2023

Choose a reason for hiding this comment

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

Your code doesn't look much different to me, but perhaps I'm misunderstanding again. E.g.

go (CostRose cost1 [CostRose cost2 [CostRose cost3 []]]) rs' ~>

CostRose cost1 $ go (CostRose cost2 [CostRose cost3 []]) ([] ++ rs') ~>

CostRose cost1 $ CostRose cost2 $ go (CostRose cost3 []) ([] ++ ([] ++ rs'))

It seems like for long chain of one-element forest we're going to end up with [] ++ ([] ++ ([] ++ <...>)), which is effectively the same, except we also pay for all the overhead of list concatenation (it's lazy, so costs are amortized, but they're still there).

If it's amortized O(1) then it's not a problem.

Yeah, and I feel like special-casing single-element forests could help us level the cost of retrieving the next element, but at the expense of introducing an additional cost for every single node, which wouldn't pay off, I think. Still, I'm going to play with it.

Copy link
Contributor Author

@effectfully effectfully Mar 29, 2023

Choose a reason for hiding this comment

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

How about this:

flattenCostRoseGo :: Int# -> CostRose -> [CostRose] -> (Int# -> CostStream) -> CostStream
flattenCostRoseGo i0 rose1 forest2 k =
    CostCons (coerce $ I# i0) $
        let !(CostRose cost1 forest1) = rose1
            !(I# i1) = coerce cost1
        in case forest1 of
            [] -> case forest2 of
                []                -> k i1
                rose2' : forest2' -> flattenCostRoseGo i1 rose2' forest2' k
            rose1' : forest1' -> case forest2 of
                []                -> flattenCostRoseGo i1 rose1' forest1' k
                rose2' : forest2' ->
                    flattenCostRoseGo i1 rose1' forest1' $ \i2 ->
                        flattenCostRoseGo i2 rose2' forest2' k

flattenCostRose :: CostRose -> CostStream
flattenCostRose (CostRose cost [])              = CostLast cost
flattenCostRose (CostRose cost (rose : forest)) =
    case coerce cost of
        I# i -> flattenCostRoseGo i rose forest $ \iz -> coerce CostLast $ I# iz
{-# INLINE flattenCostRose #-}

We essentially need to prove by induction that both flattenCostRoseGo and its continuation argument always produce an element in constant time. For flattenCostRoseGo it's entirely obvious and for the continuation we have several cases:

  1. the initial case with CostLast -- obviously constant-time
  2. the case when either of forest1 or forest2 is empty (but not both at the same time) -- we pass the continuation unchanged and hence don't change any of its behavior
  3. the case when both forest1 and forest2 are non-empty -- we do grow the continuation, but by induction hypothesis flattenCostRoseGo produces an element in constant time and so is the new continuation

I.e. we break the previous chains of empty forest handling by matching on forest2 before recursing in order not to grow the continuation with a fancy form of id. And we also don't seem to do any extra work overall, since that matching is useful (now that flattenCostRoseGo receives both a node and a remaining forest), albeit slightly premature (we may run out of budget before getting to accounting for costs from forest2, but we don't care much about optimizing the unhappy path, since it's when the smart contracts fails anyway).

What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

It seems like for long chain of one-element forest we're going to end up with [] ++ ([] ++ ([] ++ <...>)), which is effectively the same

Yeah, I think you are right.

What do you think?

Yes I think this is indeed O(1) worst case. However, I don't quite see the benefit of CPS, because in my non-CPS version, you can also match on the rs and avoid concatenation if it is empty.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

However, I don't quite see the benefit of CPS, because in my non-CPS version, you can also match on the rs and avoid concatenation if it is empty.

Yours is different. In mine we use the matching not only to check that the list is not empty, but also to retrieve its head to use it later, so in any case the matching is useful. In your case it's only useful when the list is empty, provided I got the code right:

flattenCostRoseGo :: CostRose -> [CostRose] -> CostStream
flattenCostRoseGo (CostRose cost1 forest1) forest2 =
    case forest1 of
        [] -> case forest2 of
            []                -> CostLast cost1
            rose2' : forest2' -> CostCons cost1 $ flattenCostRoseGo rose2' forest2'
        rose1' : forest1' ->
            CostCons cost1 $ case forest1' of
                [] -> flattenCostRoseGo rose1' forest2
                _  -> flattenCostRoseGo rose1' $ forest1' ++ forest2

flattenCostRose :: CostRose -> CostStream
flattenCostRose (CostRose cost [])              = CostLast cost
flattenCostRose (CostRose cost (rose : forest)) = CostCons cost $ flattenCostRoseGo rose forest
{-# INLINE flattenCostRose #-}

That is one overhead, the other one is ++. But maybe both of these overheads are negligible compared to the overhead of passing a continuation around.

I'll run the validation benchmarks with your version. If the results are the same, I'll pick it as it's much simpler than the continuation one.

Although I also wanted to try a different definition of CostRose.

@effectfully
Copy link
Contributor Author

/benchmark validation

@github-actions
Copy link
Contributor

Click here to check the status of your benchmark.

@github-actions
Copy link
Contributor

Comparing benchmark results of ' validation' on '6f3f472138' (base) and '65794ecffa' (PR)

Results table
Script 6f3f472 65794ec Change
auction_1-1 167.4 μs 163.7 μs -2.2%
auction_1-2 682.6 μs 684.5 μs +0.3%
auction_1-3 676.6 μs 682.8 μs +0.9%
auction_1-4 219.3 μs 213.6 μs -2.6%
auction_2-1 169.7 μs 166.4 μs -1.9%
auction_2-2 688.1 μs 688.3 μs +0.0%
auction_2-3 903.0 μs 901.2 μs -0.2%
auction_2-4 675.9 μs 682.5 μs +1.0%
auction_2-5 219.1 μs 213.7 μs -2.5%
crowdfunding-success-1 200.1 μs 193.8 μs -3.1%
crowdfunding-success-2 199.7 μs 194.0 μs -2.9%
crowdfunding-success-3 199.8 μs 194.1 μs -2.9%
currency-1 252.1 μs 251.2 μs -0.4%
escrow-redeem_1-1 348.7 μs 350.7 μs +0.6%
escrow-redeem_1-2 349.8 μs 348.7 μs -0.3%
escrow-redeem_2-1 415.5 μs 411.9 μs -0.9%
escrow-redeem_2-2 411.0 μs 409.7 μs -0.3%
escrow-redeem_2-3 411.1 μs 411.0 μs -0.0%
escrow-refund-1 148.6 μs 145.7 μs -2.0%
future-increase-margin-1 251.5 μs 251.3 μs -0.1%
future-increase-margin-2 549.8 μs 548.2 μs -0.3%
future-increase-margin-3 547.0 μs 546.6 μs -0.1%
future-increase-margin-4 509.4 μs 515.9 μs +1.3%
future-increase-margin-5 868.7 μs 879.7 μs +1.3%
future-pay-out-1 251.5 μs 251.6 μs +0.0%
future-pay-out-2 549.2 μs 546.2 μs -0.5%
future-pay-out-3 555.0 μs 545.3 μs -1.7%
future-pay-out-4 878.7 μs 875.7 μs -0.3%
future-settle-early-1 252.7 μs 250.8 μs -0.8%
future-settle-early-2 553.5 μs 547.1 μs -1.2%
future-settle-early-3 553.3 μs 545.2 μs -1.5%
future-settle-early-4 655.6 μs 652.9 μs -0.4%
game-sm-success_1-1 404.6 μs 402.4 μs -0.5%
game-sm-success_1-2 188.6 μs 183.2 μs -2.9%
game-sm-success_1-3 678.1 μs 672.0 μs -0.9%
game-sm-success_1-4 220.0 μs 214.4 μs -2.5%
game-sm-success_2-1 403.2 μs 400.2 μs -0.7%
game-sm-success_2-2 188.9 μs 183.8 μs -2.7%
game-sm-success_2-3 678.7 μs 670.9 μs -1.1%
game-sm-success_2-4 219.5 μs 214.0 μs -2.5%
game-sm-success_2-5 675.9 μs 670.4 μs -0.8%
game-sm-success_2-6 219.6 μs 214.0 μs -2.6%
multisig-sm-1 410.5 μs 414.6 μs +1.0%
multisig-sm-2 409.3 μs 405.2 μs -1.0%
multisig-sm-3 412.3 μs 409.4 μs -0.7%
multisig-sm-4 420.4 μs 415.5 μs -1.2%
multisig-sm-5 597.2 μs 590.8 μs -1.1%
multisig-sm-6 410.4 μs 412.3 μs +0.5%
multisig-sm-7 409.6 μs 405.7 μs -1.0%
multisig-sm-8 414.7 μs 409.9 μs -1.2%
multisig-sm-9 419.0 μs 415.9 μs -0.7%
multisig-sm-10 598.8 μs 588.5 μs -1.7%
ping-pong-1 342.7 μs 340.0 μs -0.8%
ping-pong-2 344.3 μs 340.7 μs -1.0%
ping-pong_2-1 201.7 μs 197.5 μs -2.1%
prism-1 156.3 μs 155.9 μs -0.3%
prism-2 427.8 μs 419.9 μs -1.8%
prism-3 365.4 μs 364.0 μs -0.4%
pubkey-1 133.6 μs 128.0 μs -4.2%
stablecoin_1-1 981.1 μs 963.8 μs -1.8%
stablecoin_1-2 184.7 μs 177.7 μs -3.8%
stablecoin_1-3 1.126 ms 1.104 ms -2.0%
stablecoin_1-4 194.2 μs 189.1 μs -2.6%
stablecoin_1-5 1.438 ms 1.406 ms -2.2%
stablecoin_1-6 241.9 μs 232.5 μs -3.9%
stablecoin_2-1 982.5 μs 965.4 μs -1.7%
stablecoin_2-2 183.7 μs 178.6 μs -2.8%
stablecoin_2-3 1.122 ms 1.103 ms -1.7%
stablecoin_2-4 194.1 μs 189.3 μs -2.5%
token-account-1 185.6 μs 182.0 μs -1.9%
token-account-2 333.6 μs 327.2 μs -1.9%
uniswap-1 424.0 μs 416.4 μs -1.8%
uniswap-2 216.0 μs 214.1 μs -0.9%
uniswap-3 1.872 ms 1.843 ms -1.5%
uniswap-4 313.8 μs 307.9 μs -1.9%
uniswap-5 1.200 ms 1.180 ms -1.7%
uniswap-6 303.6 μs 297.1 μs -2.1%
vesting-1 358.3 μs 355.6 μs -0.8%

@effectfully
Copy link
Contributor Author

/benchmark plutus-benchmark:validation

@github-actions
Copy link
Contributor

Click here to check the status of your benchmark.

@github-actions
Copy link
Contributor

Comparing benchmark results of ' plutus-benchmark:validation' on 'f49230f650' (base) and '80611ef09c' (PR)

Results table
Script f49230f 80611ef Change
auction_1-1 170.3 μs 164.5 μs -3.4%
auction_1-2 698.9 μs 670.3 μs -4.1%
auction_1-3 698.6 μs 673.4 μs -3.6%
auction_1-4 225.6 μs 213.3 μs -5.5%
auction_2-1 174.5 μs 166.5 μs -4.6%
auction_2-2 702.1 μs 677.6 μs -3.5%
auction_2-3 930.4 μs 889.1 μs -4.4%
auction_2-4 699.9 μs 667.6 μs -4.6%
auction_2-5 225.9 μs 212.6 μs -5.9%
crowdfunding-success-1 203.9 μs 195.1 μs -4.3%
crowdfunding-success-2 204.9 μs 194.8 μs -4.9%
crowdfunding-success-3 204.5 μs 194.4 μs -4.9%
currency-1 258.2 μs 251.0 μs -2.8%
escrow-redeem_1-1 361.6 μs 347.6 μs -3.9%
escrow-redeem_1-2 357.8 μs 348.6 μs -2.6%
escrow-redeem_2-1 425.3 μs 411.7 μs -3.2%
escrow-redeem_2-2 417.9 μs 408.1 μs -2.3%
escrow-redeem_2-3 419.8 μs 411.8 μs -1.9%
escrow-refund-1 149.9 μs 145.2 μs -3.1%
future-increase-margin-1 256.5 μs 249.9 μs -2.6%
future-increase-margin-2 560.4 μs 547.3 μs -2.3%
future-increase-margin-3 560.4 μs 548.3 μs -2.2%
future-increase-margin-4 521.8 μs 523.0 μs +0.2%
future-increase-margin-5 893.1 μs 888.3 μs -0.5%
future-pay-out-1 256.7 μs 250.3 μs -2.5%
future-pay-out-2 560.6 μs 549.6 μs -2.0%
future-pay-out-3 563.1 μs 549.5 μs -2.4%
future-pay-out-4 895.2 μs 877.8 μs -1.9%
future-settle-early-1 256.5 μs 251.6 μs -1.9%
future-settle-early-2 561.2 μs 546.2 μs -2.7%
future-settle-early-3 562.8 μs 546.7 μs -2.9%
future-settle-early-4 663.2 μs 652.6 μs -1.6%
game-sm-success_1-1 407.2 μs 398.7 μs -2.1%
game-sm-success_1-2 191.9 μs 183.2 μs -4.5%
game-sm-success_1-3 686.1 μs 665.2 μs -3.0%
game-sm-success_1-4 223.6 μs 213.9 μs -4.3%
game-sm-success_2-1 405.3 μs 395.3 μs -2.5%
game-sm-success_2-2 192.0 μs 182.1 μs -5.2%
game-sm-success_2-3 684.8 μs 663.6 μs -3.1%
game-sm-success_2-4 225.6 μs 212.8 μs -5.7%
game-sm-success_2-5 686.1 μs 664.2 μs -3.2%
game-sm-success_2-6 224.6 μs 213.9 μs -4.8%
multisig-sm-1 419.9 μs 408.8 μs -2.6%
multisig-sm-2 416.8 μs 399.9 μs -4.1%
multisig-sm-3 419.6 μs 405.0 μs -3.5%
multisig-sm-4 429.2 μs 411.3 μs -4.2%
multisig-sm-5 614.0 μs 581.8 μs -5.2%
multisig-sm-6 419.6 μs 408.4 μs -2.7%
multisig-sm-7 414.5 μs 402.0 μs -3.0%
multisig-sm-8 419.2 μs 410.9 μs -2.0%
multisig-sm-9 421.3 μs 410.8 μs -2.5%
multisig-sm-10 602.4 μs 579.5 μs -3.8%
ping-pong-1 346.2 μs 334.6 μs -3.4%
ping-pong-2 347.5 μs 335.9 μs -3.3%
ping-pong_2-1 205.4 μs 194.5 μs -5.3%
prism-1 160.3 μs 153.7 μs -4.1%
prism-2 434.9 μs 424.0 μs -2.5%
prism-3 371.4 μs 364.6 μs -1.8%
pubkey-1 138.0 μs 128.9 μs -6.6%
stablecoin_1-1 1.081 ms 981.6 μs -9.2%
stablecoin_1-2 193.0 μs 179.2 μs -7.2%
stablecoin_1-3 1.146 ms 1.116 ms -2.6%
stablecoin_1-4 201.9 μs 190.3 μs -5.7%
stablecoin_1-5 1.468 ms 1.431 ms -2.5%
stablecoin_1-6 249.0 μs 235.4 μs -5.5%
stablecoin_2-1 999.3 μs 976.8 μs -2.3%
stablecoin_2-2 188.9 μs 179.8 μs -4.8%
stablecoin_2-3 1.138 ms 1.122 ms -1.4%
stablecoin_2-4 199.5 μs 190.4 μs -4.6%
token-account-1 189.8 μs 182.2 μs -4.0%
token-account-2 338.4 μs 332.6 μs -1.7%
uniswap-1 432.7 μs 422.3 μs -2.4%
uniswap-2 219.5 μs 216.7 μs -1.3%
uniswap-3 1.947 ms 1.871 ms -3.9%
uniswap-4 325.4 μs 309.8 μs -4.8%
uniswap-5 1.259 ms 1.188 ms -5.6%
uniswap-6 314.3 μs 298.9 μs -4.9%
vesting-1 365.3 μs 359.8 μs -1.5%

@effectfully
Copy link
Contributor Author

-3.51% on average! Damn, didn't expect @zliu41's suggestion to perform that well. Gonna rerun the benchmarks to make sure it's not some kind of a glitch.

@effectfully
Copy link
Contributor Author

/benchmark plutus-benchmark:validation

@github-actions
Copy link
Contributor

Click here to check the status of your benchmark.

@github-actions
Copy link
Contributor

Comparing benchmark results of ' plutus-benchmark:validation' on 'f49230f650' (base) and '80611ef09c' (PR)

Results table
Script f49230f 80611ef Change
auction_1-1 170.9 μs 166.3 μs -2.7%
auction_1-2 695.1 μs 690.7 μs -0.6%
auction_1-3 685.8 μs 672.0 μs -2.0%
auction_1-4 224.4 μs 213.8 μs -4.7%
auction_2-1 173.2 μs 166.3 μs -4.0%
auction_2-2 690.9 μs 677.3 μs -2.0%
auction_2-3 915.2 μs 890.7 μs -2.7%
auction_2-4 691.7 μs 672.6 μs -2.8%
auction_2-5 224.8 μs 213.9 μs -4.8%
crowdfunding-success-1 203.1 μs 194.7 μs -4.1%
crowdfunding-success-2 203.2 μs 194.5 μs -4.3%
crowdfunding-success-3 203.9 μs 194.2 μs -4.8%
currency-1 257.2 μs 250.0 μs -2.8%
escrow-redeem_1-1 356.3 μs 350.6 μs -1.6%
escrow-redeem_1-2 356.9 μs 348.6 μs -2.3%
escrow-redeem_2-1 423.9 μs 408.0 μs -3.8%
escrow-redeem_2-2 420.9 μs 408.7 μs -2.9%
escrow-redeem_2-3 421.1 μs 411.4 μs -2.3%
escrow-refund-1 150.1 μs 145.8 μs -2.9%
future-increase-margin-1 256.8 μs 250.9 μs -2.3%
future-increase-margin-2 563.3 μs 550.8 μs -2.2%
future-increase-margin-3 561.1 μs 547.0 μs -2.5%
future-increase-margin-4 524.9 μs 514.8 μs -1.9%
future-increase-margin-5 893.5 μs 873.2 μs -2.3%
future-pay-out-1 256.8 μs 250.9 μs -2.3%
future-pay-out-2 563.3 μs 548.9 μs -2.6%
future-pay-out-3 562.0 μs 550.2 μs -2.1%
future-pay-out-4 890.6 μs 880.3 μs -1.2%
future-settle-early-1 257.5 μs 250.7 μs -2.6%
future-settle-early-2 562.4 μs 551.3 μs -2.0%
future-settle-early-3 563.7 μs 551.2 μs -2.2%
future-settle-early-4 664.9 μs 653.9 μs -1.7%
game-sm-success_1-1 408.6 μs 403.2 μs -1.3%
game-sm-success_1-2 192.7 μs 184.9 μs -4.0%
game-sm-success_1-3 687.6 μs 675.7 μs -1.7%
game-sm-success_1-4 223.2 μs 215.2 μs -3.6%
game-sm-success_2-1 406.0 μs 403.8 μs -0.5%
game-sm-success_2-2 192.8 μs 184.2 μs -4.5%
game-sm-success_2-3 686.2 μs 678.1 μs -1.2%
game-sm-success_2-4 223.3 μs 216.6 μs -3.0%
game-sm-success_2-5 687.7 μs 678.7 μs -1.3%
game-sm-success_2-6 225.0 μs 215.7 μs -4.1%
multisig-sm-1 416.1 μs 416.3 μs +0.0%
multisig-sm-2 415.7 μs 408.6 μs -1.7%
multisig-sm-3 418.8 μs 413.5 μs -1.3%
multisig-sm-4 424.4 μs 424.2 μs -0.0%
multisig-sm-5 606.7 μs 596.7 μs -1.6%
multisig-sm-6 418.2 μs 419.0 μs +0.2%
multisig-sm-7 414.4 μs 409.1 μs -1.3%
multisig-sm-8 420.3 μs 415.4 μs -1.2%
multisig-sm-9 423.8 μs 417.6 μs -1.5%
multisig-sm-10 606.8 μs 589.9 μs -2.8%
ping-pong-1 348.3 μs 339.2 μs -2.6%
ping-pong-2 351.5 μs 341.6 μs -2.8%
ping-pong_2-1 204.3 μs 196.8 μs -3.7%
prism-1 161.4 μs 156.0 μs -3.3%
prism-2 436.4 μs 430.0 μs -1.5%
prism-3 370.5 μs 366.5 μs -1.1%
pubkey-1 136.0 μs 129.4 μs -4.9%
stablecoin_1-1 990.2 μs 992.6 μs +0.2%
stablecoin_1-2 189.1 μs 181.6 μs -4.0%
stablecoin_1-3 1.143 ms 1.127 ms -1.4%
stablecoin_1-4 199.2 μs 191.6 μs -3.8%
stablecoin_1-5 1.449 ms 1.442 ms -0.5%
stablecoin_1-6 247.1 μs 237.0 μs -4.1%
stablecoin_2-1 996.7 μs 981.5 μs -1.5%
stablecoin_2-2 188.9 μs 180.1 μs -4.7%
stablecoin_2-3 1.136 ms 1.125 ms -1.0%
stablecoin_2-4 199.4 μs 191.5 μs -4.0%
token-account-1 189.8 μs 182.9 μs -3.6%
token-account-2 338.8 μs 331.2 μs -2.2%
uniswap-1 432.4 μs 422.0 μs -2.4%
uniswap-2 219.6 μs 217.2 μs -1.1%
uniswap-3 1.905 ms 1.880 ms -1.3%
uniswap-4 323.4 μs 314.0 μs -2.9%
uniswap-5 1.228 ms 1.205 ms -1.9%
uniswap-6 312.8 μs 301.2 μs -3.7%
vesting-1 365.7 μs 363.8 μs -0.5%

@effectfully
Copy link
Contributor Author

-2.39% on average now. That's a bit more reasonable, although still a noticeable improvement. Let's try something entirely different now.

@effectfully
Copy link
Contributor Author

/benchmark plutus-benchmark:validation

@github-actions
Copy link
Contributor

Click here to check the status of your benchmark.

@github-actions
Copy link
Contributor

Comparing benchmark results of ' plutus-benchmark:validation' on 'f49230f650' (base) and 'f403d66351' (PR)

Results table
Script f49230f f403d66 Change
auction_1-1 170.7 μs 163.9 μs -4.0%
auction_1-2 699.2 μs -100.0%
auction_1-3 694.7 μs -100.0%
auction_1-4 226.5 μs -100.0%
auction_2-1 175.4 μs -100.0%
auction_2-2 698.6 μs -100.0%
auction_2-3 925.0 μs -100.0%
auction_2-4 693.4 μs -100.0%
auction_2-5 224.4 μs -100.0%
crowdfunding-success-1 202.6 μs -100.0%
crowdfunding-success-2 203.5 μs -100.0%
crowdfunding-success-3 202.6 μs -100.0%
currency-1 256.2 μs -100.0%
escrow-redeem_1-1 355.7 μs -100.0%
escrow-redeem_1-2 353.1 μs -100.0%
escrow-redeem_2-1 420.9 μs -100.0%
escrow-redeem_2-2 419.9 μs -100.0%
escrow-redeem_2-3 418.5 μs -100.0%
escrow-refund-1 151.1 μs -100.0%
future-increase-margin-1 255.8 μs -100.0%
future-increase-margin-2 560.9 μs -100.0%
future-increase-margin-3 560.0 μs -100.0%
future-increase-margin-4 521.9 μs -100.0%
future-increase-margin-5 893.4 μs -100.0%
future-pay-out-1 256.8 μs -100.0%
future-pay-out-2 560.6 μs -100.0%
future-pay-out-3 560.6 μs -100.0%
future-pay-out-4 889.3 μs -100.0%
future-settle-early-1 255.1 μs -100.0%
future-settle-early-2 559.2 μs -100.0%
future-settle-early-3 562.0 μs -100.0%
future-settle-early-4 665.8 μs -100.0%
game-sm-success_1-1 412.4 μs -100.0%
game-sm-success_1-2 192.5 μs -100.0%
game-sm-success_1-3 684.7 μs -100.0%
game-sm-success_1-4 222.5 μs -100.0%
game-sm-success_2-1 404.8 μs -100.0%
game-sm-success_2-2 194.2 μs -100.0%
game-sm-success_2-3 685.7 μs -100.0%
game-sm-success_2-4 222.9 μs -100.0%
game-sm-success_2-5 683.5 μs -100.0%
game-sm-success_2-6 223.1 μs -100.0%
multisig-sm-1 417.2 μs -100.0%
multisig-sm-2 414.4 μs -100.0%
multisig-sm-3 417.3 μs -100.0%
multisig-sm-4 424.9 μs -100.0%
multisig-sm-5 607.0 μs -100.0%
multisig-sm-6 420.4 μs -100.0%
multisig-sm-7 415.8 μs -100.0%
multisig-sm-8 419.2 μs -100.0%
multisig-sm-9 422.4 μs -100.0%
multisig-sm-10 605.8 μs -100.0%
ping-pong-1 346.0 μs -100.0%
ping-pong-2 348.7 μs -100.0%
ping-pong_2-1 204.9 μs -100.0%
prism-1 161.8 μs -100.0%
prism-2 435.7 μs -100.0%
prism-3 370.2 μs -100.0%
pubkey-1 136.0 μs -100.0%
stablecoin_1-1 995.2 μs -100.0%
stablecoin_1-2 188.6 μs -100.0%
stablecoin_1-3 1.136 ms -100.0%
stablecoin_1-4 200.2 μs -100.0%
stablecoin_1-5 1.446 ms -100.0%
stablecoin_1-6 247.7 μs -100.0%
stablecoin_2-1 994.3 μs -100.0%
stablecoin_2-2 188.1 μs -100.0%
stablecoin_2-3 1.133 ms -100.0%
stablecoin_2-4 199.7 μs -100.0%
token-account-1 189.5 μs -100.0%
token-account-2 336.6 μs -100.0%
uniswap-1 426.7 μs -100.0%
uniswap-2 217.2 μs -100.0%
uniswap-3 1.876 ms -100.0%
uniswap-4 319.2 μs -100.0%
uniswap-5 1.206 ms -100.0%
uniswap-6 309.1 μs -100.0%
vesting-1 359.4 μs -100.0%

@effectfully
Copy link
Contributor Author

Eek, forgot an undefined in the code. Rerunning.

@effectfully effectfully force-pushed the effectfully/builtins/lazy-costing branch from 338011c to 8d9da85 Compare April 13, 2023 17:19
@effectfully
Copy link
Contributor Author

I've addressed all the comments that we can't address later (a few not-too-important ones are left as future work) and documented everything, including the Costing.hs module with tests. If you haven't already reviewed the latter, please do it.

Otherwise should be ready for merging.

@effectfully
Copy link
Contributor Author

/benchmark validation

@effectfully
Copy link
Contributor Author

Otherwise should be ready for merging.

Although I'm going to take a final look today just in case.

@kwxm
Copy link
Contributor

kwxm commented Apr 13, 2023

Manual benchmark results for this branch. Looks good.

Script                              Strict         Lazy        Change
----------------------------------------------------------------------------------
auction_1-1                       179.6 μs	 176.3 μs      -1.8%
auction_1-2                       743.8 μs	 721.6 μs      -3.0%
auction_1-3                       733.8 μs	 712.8 μs      -2.9%
auction_1-4                       234.8 μs	 228.7 μs      -2.6%
auction_2-1                       182.9 μs	 179.5 μs      -1.9%
auction_2-2                       744.6 μs	 721.4 μs      -3.1%
auction_2-3                       988.7 μs	 947.7 μs      -4.1%
auction_2-4                       740.8 μs	 714.6 μs      -3.5%
auction_2-5                       236.9 μs	 228.9 μs      -3.4%
crowdfunding-success-1            215.9 μs	 211.8 μs      -1.9%
crowdfunding-success-2            215.7 μs	 212.0 μs      -1.7%
crowdfunding-success-3            216.1 μs	 212.8 μs      -1.5%
currency-1                        277.6 μs	 267.5 μs      -3.6%
escrow-redeem_1-1                 383.0 μs	 372.3 μs      -2.8%
escrow-redeem_1-2                 385.3 μs	 375.3 μs      -2.6%
escrow-redeem_2-1                 450.5 μs	 435.2 μs      -3.4%
escrow-redeem_2-2                 445.5 μs	 434.2 μs      -2.5%
escrow-redeem_2-3                 447.1 μs	 434.0 μs      -2.9%
escrow-refund-1                   159.2 μs	 156.7 μs      -1.6%
future-increase-margin-1          277.6 μs	 266.8 μs      -3.9%
future-increase-margin-2          601.3 μs	 581.3 μs      -3.3%
future-increase-margin-3          608.2 μs	 584.5 μs      -3.9%
future-increase-margin-4          556.6 μs	 542.6 μs      -2.5%
future-increase-margin-5          948.0 μs	 922.5 μs      -2.7%
future-pay-out-1                  278.3 μs	 266.6 μs      -4.2%
future-pay-out-2                  603.4 μs	 577.9 μs      -4.2%
future-pay-out-3                  610.7 μs	 581.9 μs      -4.7%
future-pay-out-4                  948.6 μs	 914.9 μs      -3.6%
future-settle-early-1             277.6 μs	 266.2 μs      -4.1%
future-settle-early-2             605.9 μs	 577.6 μs      -4.7%
future-settle-early-3             606.5 μs	 583.0 μs      -3.9%
future-settle-early-4             701.8 μs	 684.1 μs      -2.5%
game-sm-success_1-1               433.2 μs	 425.3 μs      -1.8%
game-sm-success_1-2               203.8 μs	 197.2 μs      -3.2%
game-sm-success_1-3               735.0 μs	 715.6 μs      -2.6%
game-sm-success_1-4               236.5 μs	 230.9 μs      -2.4%
game-sm-success_2-1               430.5 μs	 425.2 μs      -1.2%
game-sm-success_2-2               203.8 μs	 197.9 μs      -2.9%
game-sm-success_2-3               731.7 μs	 716.7 μs      -2.1%
game-sm-success_2-4               239.0 μs	 231.4 μs      -3.2%
game-sm-success_2-5               726.0 μs	 713.8 μs      -1.7%
game-sm-success_2-6               236.4 μs	 230.6 μs      -2.5%
multisig-sm-1                     447.8 μs	 430.1 μs      -4.0%
multisig-sm-2                     438.2 μs	 420.3 μs      -4.1%
multisig-sm-3                     444.6 μs	 426.9 μs      -4.0%
multisig-sm-4                     449.8 μs	 434.1 μs      -3.5%
multisig-sm-5                     643.1 μs	 624.1 μs      -3.0%
multisig-sm-6                     446.7 μs	 430.3 μs      -3.7%
multisig-sm-7                     434.3 μs	 420.2 μs      -3.2%
multisig-sm-8                     443.5 μs	 426.6 μs      -3.8%
multisig-sm-9                     447.7 μs	 430.8 μs      -3.8%
multisig-sm-10                    641.8 μs	 621.0 μs      -3.2%
ping-pong-1                       372.4 μs	 360.2 μs      -3.3%
ping-pong-2                       371.9 μs	 359.9 μs      -3.2%
ping-pong_2-1                     218.4 μs	 208.7 μs      -4.4%
prism-1                           171.8 μs	 165.6 μs      -3.6%
prism-2                           467.0 μs	 444.8 μs      -4.8%
prism-3                           398.5 μs	 384.7 μs      -3.5%
pubkey-1                          145.2 μs	 140.1 μs      -3.5%
stablecoin_1-1                    1.043 ms	 1.015 ms      -2.7%
stablecoin_1-2                    199.6 μs	 193.3 μs      -3.2%
stablecoin_1-3                    1.199 ms	 1.165 ms      -2.8%
stablecoin_1-4                    211.7 μs	 206.1 μs      -2.6%
stablecoin_1-5                    1.525 ms	 1.484 ms      -2.7%
stablecoin_1-6                    261.5 μs	 254.1 μs      -2.8%
stablecoin_2-1                    1.045 ms	 1.013 ms      -3.1%
stablecoin_2-2                    198.9 μs	 193.8 μs      -2.6%
stablecoin_2-3                    1.194 ms	 1.170 ms      -2.0%
stablecoin_2-4                    211.2 μs	 205.9 μs      -2.5%
token-account-1                   202.2 μs	 196.3 μs      -2.9%
token-account-2                   371.4 μs	 356.2 μs      -4.1%
uniswap-1                         466.2 μs	 441.5 μs      -5.3%
uniswap-2                         238.7 μs	 228.5 μs      -4.3%
uniswap-3                         2.005 ms	 1.933 ms      -3.6%
uniswap-4                         343.1 μs	 332.1 μs      -3.2%
uniswap-5                         1.297 ms	 1.250 ms      -3.6%
uniswap-6                         329.5 μs	 319.4 μs      -3.1%
vesting-1                         388.9 μs	 383.7 μs      -1.3%

@effectfully
Copy link
Contributor Author

-3.12% on average. Looks fine indeed. Plus I believe we can squeeze out more, given that we didn't implement a bunch of optimizations discussed above.

@effectfully effectfully merged commit 1ef764f into master Apr 16, 2023
@effectfully effectfully deleted the effectfully/builtins/lazy-costing branch April 16, 2023 19:43
v0d1ch pushed a commit to v0d1ch/plutus that referenced this pull request Dec 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants