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

Add a bechmark for adding transactions to the mempool #4400

Merged
merged 5 commits into from
Feb 28, 2023

Conversation

dnadales
Copy link
Member

@dnadales dnadales commented Feb 24, 2023

Part of #4382

Checklist

  • Branch
    • Commit sequence broadly makes sense
    • Commits have useful messages
    • The documentation has been properly updated
    • New tests are added if needed and existing tests are updated
    • Any changes affecting Consensus packages must have an entry in the appropriate changelog.d directory created using scriv. If in doubt, see the Consensus release process.
    • If this branch changes Network and has any consequences for downstream repositories or end users, said changes must be documented in interface-CHANGELOG.md
    • If serialization changes, user-facing consequences (e.g. replay from genesis) are confirmed to be intentional.
  • Pull Request
    • Self-reviewed the diff
    • Useful pull request description at least containing the following information:
      • What does this PR change?
      • Why these changes were needed?
      • How does this affect downstream repositories and/or end-users?
      • Which ticket does this PR close (if any)? If it does, is it linked?
    • Reviewer requested

@dnadales dnadales requested review from abailly-iohk, bartfrenk and jorisdral and removed request for nfrisby February 24, 2023 11:08
a convenience function to create an override for the mempool capacity
using the provided number bytes.
@dnadales dnadales force-pushed the dnadales/4382-add-getSnapshotFor-benchmarks branch from 1ce84b2 to ead040a Compare February 24, 2023 11:55
Copy link
Contributor

@abailly-iohk abailly-iohk left a comment

Choose a reason for hiding this comment

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

LGTM! Ideally we would like to dump/publish those results somewhere and enable comparisons across changes, otherwise I am afraid we won't reap the benefits of this approach.

Copy link
Contributor

@jorisdral jorisdral left a comment

Choose a reason for hiding this comment

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

Added some remarks!

EDIT: I've noticed that CI does not build yet build benchmark suites, so we should probably enable that

@@ -326,3 +326,45 @@ test-suite test-infra
-Wredundant-constraints
-Wmissing-export-lists
-fno-ignore-asserts

benchmark bench-mempool
Copy link
Contributor

Choose a reason for hiding this comment

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

If we plan on writing more benchmarks for other consensus components, would we want to have a single benchmark executable or one for each component that we target?

Copy link
Member Author

Choose a reason for hiding this comment

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

What about one per component? Why? To keep them isolated. But I don't know if the other approach as more advantages. Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not opposed to having one per component. An advantage to one executable for all is that we can reuse code, but I don't know how often we will actually be in a situation where we can reuse code. I think one advantage of having multiple executables is that benchmark running times tend to be on the lengthy side, so not having one monolith executable that takes ages to run is probably a good thing.

-Wredundant-constraints
-Wmissing-export-lists
-Wno-unticked-promoted-constructors
-fno-ignore-asserts
Copy link
Contributor

Choose a reason for hiding this comment

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

If the code under benchmark contains relatively costly assertions, not ignoring assertions might inflate the results. This is just something to take into consideration, it is not something that should change now

Copy link
Member Author

Choose a reason for hiding this comment

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

True! I missed that. Thanks!

Comment on lines +3 to +4
- Added `mkCapacityBytesOverride`, a convenience function to create an override
for the mempool capacity using the provided number bytes.
Copy link
Contributor

Choose a reason for hiding this comment

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

Did we decide on present or past tense? I'm mainly using present tense

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point. We have no consistency at the moment. I'll raise this with the team.

Comment on lines 362 to 363
-- | Apply the payload to a ticked state directly to the payload dependent state
-- portion of it, leaving the rest of the input ticked state unaltered.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
-- | Apply the payload to a ticked state directly to the payload dependent state
-- portion of it, leaving the rest of the input ticked state unaltered.
-- | Apply the payload directly to the payload dependent state
-- portion of a ticked state , leaving the rest of the input ticked state unaltered.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is much simpler ❤️ Thanks!

(Ticked (LedgerState (TestBlockWith ptype)))
applyDirectlyToPayloadDependentState (TickedTestLedger st) tx = do
payloadDepSt' <- applyPayload (payloadDependentState st) tx
pure $ TickedTestLedger $ st { payloadDependentState = payloadDepSt' }
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pure $ TickedTestLedger $ st { payloadDependentState = payloadDepSt' }
pure $ TickedTestLedger $ st { payloadDependentState = payloadDepSt' }

length mempoolTxs @?= n
]
where
benchAddNTxs n = bench (show n) $ nfIO $ addNTxs' $ mkNTryAddTxs n
Copy link
Contributor

Choose a reason for hiding this comment

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

If the cost of adding the txs is what you want to benchmark, and not opening the mocked mempool or creating the mocked transactions you want to add, I would suggest using env/envWithCleanup to do that work before running the benchmark, and then only calling the function-under-bench with those inputs. The inputs will by then already have been forced to normal form by env, such that the benchmark only measures the time spent adding the txs. Using env might not be strictly useful if the setup is much cheaper than adding the txs, but I don't think it hurts to use env regardless

It would look something like:

Suggested change
benchAddNTxs n = bench (show n) $ nfIO $ addNTxs' $ mkNTryAddTxs n
benchAddNTxs n = env (doSetup n) $ \(mempool, txs) ->
bench (show n) $ nfIO $ addNTxs' mempool

Copy link
Member Author

Choose a reason for hiding this comment

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

Absolutely! I overlooked this. Thank you 🙏

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, env's documentation recommends using withResource, which does not seem to perform any normal form evaluation.

Copy link
Member Author

@dnadales dnadales Feb 27, 2023

Choose a reason for hiding this comment

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

I hope I never forget to use a function like env when benchmarking.

Before env:

image

With env:

image

Copy link
Contributor

Choose a reason for hiding this comment

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

Generating transactions looked relatively cheap, so maybe opening the mempool was the costly part

Comment on lines 27 to 35
[ testCase "Transactions are added" $ do
let
n = 1000
txs = mkNTryAddTxs 1000
mempool <- addNTxs txs
mempoolTxs <- getTxs mempool
mempoolTxs @?= txsAddedInCmds txs
length mempoolTxs @?= n
]
Copy link
Contributor

@jorisdral jorisdral Feb 24, 2023

Choose a reason for hiding this comment

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

To be sure, I would run this test for each of the n you use in the benchmarks above here, e.g., by moving the test into benchAddNTxs. Each benchmark then has a sanity check

Another addition would be to add a regular quickcheck test that runs the test for arbitrary values of n, such that we're also leveraging property testing in addition to sanity checks for the benchmarks

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point. In this case, I wanted to avoid the cost of having to test each of the values of n. Eventually, we should test the interpretation of commands by means of the mempool property tests, but I think it makes sense to test each case.

-------------------------------------------------------------------------------}

addNTxs' :: [MempoolCmd TestBlock] -> IO ()
addNTxs' = void . addNTxs
Copy link
Contributor

Choose a reason for hiding this comment

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

I can't check now because the module containing MemppolWithMockedLedgerItf is not available on the branch, but could the void here be preventing some evaluation inside the mempool that we would want to force?

Copy link
Member Author

Choose a reason for hiding this comment

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

I removed this function in favour of the new approach using env.

@dnadales dnadales force-pushed the dnadales/4382-add-getSnapshotFor-benchmarks branch from ead040a to 3ecdba7 Compare February 24, 2023 14:09

import Ouroboros.Consensus.Mempool (Mempool)
import qualified Ouroboros.Consensus.Mempool as Mempool

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change

@dnadales
Copy link
Member Author

LGTM! Ideally we would like to dump/publish those results somewhere and enable comparisons across changes, otherwise I am afraid we won't reap the benefits of this approach.

Makes sense. Do you have any references I could look into to see how this is done? We can do this in a following step.

@abailly-iohk
Copy link
Contributor

We can definitely do this in a following step. The way I do it is usually quite simple: generate some markdown file containing your data, publish it as an artifact as part of the CI, then download it somewhere else (eg. in the docs) for publication.

@dnadales
Copy link
Member Author

bors r+

@iohk-bors iohk-bors bot merged commit 2aa7ee9 into master Feb 28, 2023
@iohk-bors iohk-bors bot deleted the dnadales/4382-add-getSnapshotFor-benchmarks branch February 28, 2023 13:47
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.

3 participants