-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
bench-cli: Add a bench CLI tool for kademlia
Signed-off-by: Alexandru Vasile <[email protected]>
- Loading branch information
Showing
7 changed files
with
328 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
[package] | ||
name = "subp2p-explorer-bench-cli" | ||
version.workspace = true | ||
authors.workspace = true | ||
edition.workspace = true | ||
rust-version.workspace = true | ||
repository.workspace = true | ||
homepage.workspace = true | ||
license.workspace = true | ||
description = "Command line utilities for working with subp2p-explorer" | ||
|
||
[[bin]] | ||
name = "bench-cli" | ||
path = "src/main.rs" | ||
|
||
[dependencies] | ||
subp2p-explorer-core = { workspace = true } | ||
tokio = { workspace = true, features = ["macros", "time", "rt-multi-thread"] } | ||
async-trait = { workspace = true } | ||
env_logger = { workspace = true } | ||
tracing-subscriber = { workspace = true } | ||
tracing = { workspace = true } | ||
futures = { workspace = true } | ||
libp2p = { workspace = true, features = ["dns", "identify", "kad", "macros", "mdns", "noise", "ping", "tcp", "tokio", "yamux", "websocket", "request-response"] } | ||
rand = { workspace = true } | ||
fnv = { workspace = true } | ||
log = { workspace = true } | ||
either = { workspace = true } | ||
void = { workspace = true } | ||
pin-project = { workspace = true } | ||
asynchronous-codec = { workspace = true } | ||
unsigned-varint = { workspace = true, features = ["futures", "asynchronous_codec"] } | ||
thiserror = { workspace = true } | ||
bytes = { workspace = true } | ||
codec = { package = "parity-scale-codec", workspace = true, features = ["derive"] } | ||
primitive-types = { workspace = true, default-features = false, features = ["codec", "scale-info", "serde"] } | ||
serde_json = { workspace = true } | ||
hex = { workspace = true } | ||
clap = { workspace = true } | ||
ip_network = { workspace = true } | ||
maxminddb = { workspace = true } | ||
trust-dns-resolver = { workspace = true } | ||
multihash-codetable = { workspace = true, features = ["digest", "serde", "sha2"] } | ||
jsonrpsee = { workspace = true, features = ["async-client", "client-ws-transport-native-tls"] } | ||
ss58-registry = { version = "1.34.0", default-features = false } | ||
serde = { workspace = true, features = ["derive"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
|
||
# Benchmarking CLI | ||
|
||
The benchmarking CLI is designed to benchmark the performance of different network backends. | ||
|
||
This data helps us drive improvements in various network components, such as the discovery process. | ||
|
||
At the moment the CLI supports the following backends: | ||
|
||
- [x] litep2p | ||
- [x] libp2p | ||
|
||
## Usage | ||
|
||
### Discovery Kademlia Benchmarking (live chains) | ||
|
||
Although the tool is targeting a live chain, it can also be used to benchmark the discovery process on a local chain by providing the genesis block hash and at least one boot node. | ||
|
||
Local chains can be started with other tools, like zombie-net-cli, and is out of the scope of this tool. | ||
|
||
- Benchmark the discovery process on kusama using the litep2p backend with 100 peers: | ||
|
||
```bash | ||
cargo run -- discovery --genesis b0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe --bootnodes /dns/kusama-bootnode-0.polkadot.io/tcp/30333/p2p/12D3KooWSueCPH3puP2PcvqPJdNaDNF3jMZjtJtDiSy35pWrbt5h --backend-type litep2p --num-peers 100 | ||
``` | ||
|
||
- Benchmark the discovery process on kusama using the libp2p backend with 100 peers: | ||
|
||
```bash | ||
cargo run -- discovery --genesis b0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe --bootnodes /dns/kusama-bootnode-0.polkadot.io/tcp/30333/p2p/12D3KooWSueCPH3puP2PcvqPJdNaDNF3jMZjtJtDiSy35pWrbt5h --backend-type litep2p --num-peers 100 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
use std::collections::{HashMap, HashSet}; | ||
use std::error::Error; | ||
|
||
use futures::StreamExt; | ||
use subp2p_explorer_core::{ | ||
types::{multiaddr::Multiaddr, peer_id::PeerId}, | ||
NetworkEvent, | ||
}; | ||
|
||
use crate::DiscoverBackendsNetworkOpts; | ||
|
||
pub async fn discovery_backends(opts: DiscoverBackendsNetworkOpts) -> Result<(), Box<dyn Error>> { | ||
let mut peers_data = HashMap::new(); | ||
let mut queries = HashSet::new(); | ||
|
||
// Parse the provided bootnodes as `PeerId` and `MultiAddress`. | ||
let bootnodes: Vec<_> = opts | ||
.bootnodes | ||
.iter() | ||
.map(|bootnode| { | ||
let parts: Vec<_> = bootnode.split('/').collect(); | ||
let peer = parts.last().expect("Valid bootnode has peer; qed"); | ||
let multiaddress: Multiaddr = bootnode.parse().expect("Valid multiaddress; qed"); | ||
let peer_id: PeerId = peer.parse().expect("Valid peer ID; qed"); | ||
|
||
log::info!("Bootnode peer={:?}", peer_id); | ||
(peer_id, multiaddress) | ||
}) | ||
.collect(); | ||
|
||
let now = std::time::Instant::now(); | ||
|
||
match opts.backend_type { | ||
crate::BackendType::Litep2p => { | ||
let mut backend = subp2p_explorer_core::litep2p::Litep2pBackend::new(opts.genesis); | ||
|
||
for (peer_id, address) in bootnodes { | ||
backend | ||
.add_known_peer(peer_id, std::iter::once(address)) | ||
.await; | ||
} | ||
|
||
let num_peers = opts.num_peers; | ||
|
||
log::info!("Discovering peers..."); | ||
for _ in 0..50 { | ||
let query_id = backend.find_node(PeerId::random()).await; | ||
queries.insert(query_id); | ||
} | ||
|
||
while let Some(event) = backend.next().await { | ||
match event { | ||
NetworkEvent::PeerIdentified { | ||
peer, | ||
listen_addresses, | ||
.. | ||
} => { | ||
let entry = peers_data.entry(peer).or_insert_with(|| HashSet::new()); | ||
listen_addresses.into_iter().for_each(|address| { | ||
entry.insert(address); | ||
}); | ||
} | ||
NetworkEvent::FindNode { | ||
peers, query_id, .. | ||
} => { | ||
log::info!(" Kademlia query finished {:?}", query_id); | ||
queries.remove(&query_id); | ||
|
||
peers.into_iter().for_each(|(peer, addresses)| { | ||
let entry = peers_data.entry(peer).or_insert_with(|| HashSet::new()); | ||
addresses.into_iter().for_each(|address| { | ||
entry.insert(address); | ||
}); | ||
}); | ||
} | ||
} | ||
|
||
log::info!("Discovered {}/{} peers", peers_data.len(), num_peers); | ||
|
||
if peers_data.len() >= num_peers { | ||
break; | ||
} | ||
|
||
while queries.len() < 50 { | ||
let query_id = backend.find_node(PeerId::random()).await; | ||
queries.insert(query_id); | ||
} | ||
} | ||
} | ||
crate::BackendType::Libp2p => { | ||
let mut backend = subp2p_explorer_core::libp2p::Libp2pBackend::new(opts.genesis); | ||
|
||
for (peer_id, address) in bootnodes { | ||
backend | ||
.add_known_peer(peer_id, std::iter::once(address)) | ||
.await; | ||
} | ||
|
||
let num_peers = opts.num_peers; | ||
|
||
log::info!("Discovering peers..."); | ||
for _ in 0..50 { | ||
let query_id = backend.find_node(PeerId::random()).await; | ||
queries.insert(query_id); | ||
} | ||
|
||
while let Some(event) = backend.next().await { | ||
match event { | ||
NetworkEvent::PeerIdentified { | ||
peer, | ||
listen_addresses, | ||
.. | ||
} => { | ||
let entry = peers_data.entry(peer).or_insert_with(|| HashSet::new()); | ||
listen_addresses.into_iter().for_each(|address| { | ||
entry.insert(address); | ||
}); | ||
} | ||
NetworkEvent::FindNode { | ||
peers, query_id, .. | ||
} => { | ||
log::info!(" Kademlia query finished {:?}", query_id); | ||
queries.remove(&query_id); | ||
|
||
peers.into_iter().for_each(|(peer, addresses)| { | ||
let entry = peers_data.entry(peer).or_insert_with(|| HashSet::new()); | ||
addresses.into_iter().for_each(|address| { | ||
entry.insert(address); | ||
}); | ||
}); | ||
} | ||
} | ||
|
||
log::info!("Discovered {}/{} peers", peers_data.len(), num_peers); | ||
|
||
if peers_data.len() >= num_peers { | ||
break; | ||
} | ||
|
||
while queries.len() < 50 { | ||
let query_id = backend.find_node(PeerId::random()).await; | ||
queries.insert(query_id); | ||
} | ||
} | ||
} | ||
}; | ||
|
||
log::info!("Discovery took {:?}", now.elapsed()); | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright 2023 Alexandru Vasile | ||
// This file is dual-licensed as Apache-2.0 or GPL-3.0. | ||
// see LICENSE for license details. | ||
|
||
use clap::Parser as ClapParser; | ||
use serde::Serialize; | ||
use std::error::Error; | ||
|
||
mod discovery_backends; | ||
|
||
/// Command for interacting with the CLI. | ||
#[derive(Debug, ClapParser)] | ||
enum Command { | ||
Discovery(DiscoverBackendsNetworkOpts), | ||
} | ||
|
||
#[derive(Debug, Default, clap::ValueEnum, Clone, Copy, Serialize)] | ||
#[serde(rename_all = "lowercase")] | ||
enum BackendType { | ||
/// Use the `litep2p` backend. | ||
#[default] | ||
Litep2p, | ||
|
||
/// Use the `libp2p` backend. | ||
Libp2p, | ||
} | ||
|
||
/// Discover the p2p network. | ||
#[derive(Debug, ClapParser)] | ||
pub struct DiscoverBackendsNetworkOpts { | ||
/// Hex-encoded genesis hash of the chain. | ||
/// | ||
/// For example, "781e4046b4e8b5e83d33dde04b32e7cb5d43344b1f19b574f6d31cbbd99fe738" | ||
#[clap(long, short)] | ||
genesis: String, | ||
|
||
/// Bootnodes of the chain, must contain a multiaddress together with the peer ID. | ||
/// For example, "/ip4/127.0.0.1/tcp/30333/ws/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp". | ||
#[clap(long, use_value_delimiter = true, value_parser)] | ||
bootnodes: Vec<String>, | ||
|
||
/// The number of peers discovered after which the discovery process should stop. | ||
#[clap(long, short)] | ||
num_peers: usize, | ||
|
||
/// The backend type to use for the discovery process. | ||
#[clap(long, short)] | ||
backend_type: BackendType, | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<(), Box<dyn Error>> { | ||
tracing_subscriber::fmt() | ||
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) | ||
.init(); | ||
|
||
let args = Command::parse(); | ||
match args { | ||
Command::Discovery(opts) => discovery_backends::discovery_backends(opts).await, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters