203 lines
7.7 KiB
Rust
203 lines
7.7 KiB
Rust
#![expect(
|
|
clippy::redundant_pub_crate,
|
|
reason = "criterion bench entry points are re-exported only to the private bench root module"
|
|
)]
|
|
|
|
use super::support::{
|
|
BT_TORRENT_FIXTURE, BenchmarkId, Criterion, InProcessRpcDispatcher, RpcMethod, RpcValue,
|
|
RuntimeConfig, Throughput, TorrentPeerModel, TrackerPeerListModel, TrackerResponseModel,
|
|
rpc_request,
|
|
};
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
struct BtVisibilityPressureScenario {
|
|
task_count: usize,
|
|
rounds: usize,
|
|
}
|
|
|
|
fn seed_bt_visibility_dispatcher(
|
|
scenario: BtVisibilityPressureScenario,
|
|
) -> (InProcessRpcDispatcher, Vec<String>) {
|
|
let runtime = RuntimeConfig {
|
|
allow_jsonrpc: true,
|
|
allow_xmlrpc: true,
|
|
split: 4,
|
|
max_connections_per_server: 4,
|
|
max_connection_per_server: 4,
|
|
min_split_size: 1024,
|
|
piece_length: 1024,
|
|
..RuntimeConfig::default()
|
|
};
|
|
let mut dispatcher = InProcessRpcDispatcher::with_runtime(runtime);
|
|
let mut gids = Vec::with_capacity(scenario.task_count);
|
|
for index in 0..scenario.task_count {
|
|
let add_response = dispatcher.dispatch_json(rpc_request(
|
|
RpcMethod::Aria2AddTorrent,
|
|
vec![RpcValue::String(BT_TORRENT_FIXTURE.to_owned())],
|
|
));
|
|
let gid = match add_response.result {
|
|
Some(RpcValue::String(gid)) => gid,
|
|
other => panic!("unexpected addTorrent result in visibility seed: {other:?}"),
|
|
};
|
|
dispatcher
|
|
.apply_tracker_announce_result(
|
|
&gid,
|
|
&TrackerResponseModel {
|
|
peers: TrackerPeerListModel {
|
|
interval_sec: 900,
|
|
peers: vec![TorrentPeerModel {
|
|
ip: format!("198.51.100.{}", (index % 200) + 1),
|
|
port: 51413 + (index as u16 % 32),
|
|
peer_id: None,
|
|
client_name: None,
|
|
interested: false,
|
|
choked: false,
|
|
}],
|
|
min_interval_sec: None,
|
|
tracker_id: Some(format!("bench-{index}")),
|
|
},
|
|
scrape: None,
|
|
},
|
|
)
|
|
.expect("tracker seed should populate visible bt peers");
|
|
dispatcher
|
|
.apply_bt_runtime_tick(
|
|
&gid,
|
|
8_192 + (index as u64 % 4) * 2_048,
|
|
if index % 3 == 0 { 4_096 } else { 0 },
|
|
512 + index as u64,
|
|
128 + index as u64,
|
|
if index % 3 == 0 { 5 } else { 0 },
|
|
if index % 3 == 0 { 5 } else { 0 },
|
|
index % 3 == 0,
|
|
Some(2 + (index % 6) as u32),
|
|
)
|
|
.expect("runtime tick should seed visible bt progress");
|
|
if index % 3 == 0 {
|
|
dispatcher
|
|
.set_bt_seeding_state(&gid, true, Some(1_000 + index as u64))
|
|
.expect("seeded torrents should enter seeding");
|
|
dispatcher
|
|
.tick_bt_runtime_clock(&gid, 1_010 + index as u64, true)
|
|
.expect("seeded torrents should advance share clocks");
|
|
}
|
|
gids.push(gid);
|
|
}
|
|
(dispatcher, gids)
|
|
}
|
|
|
|
fn run_bt_visibility_pressure(
|
|
dispatcher: &mut InProcessRpcDispatcher,
|
|
gids: &[String],
|
|
scenario: BtVisibilityPressureScenario,
|
|
) -> usize {
|
|
let mut calls = 0_usize;
|
|
for round in 0..scenario.rounds {
|
|
for (index, gid) in gids.iter().enumerate() {
|
|
dispatcher
|
|
.apply_bt_runtime_tick(
|
|
gid,
|
|
if round % 2 == 0 { 512 } else { 0 },
|
|
if round % 2 == 1 { 256 } else { 0 },
|
|
1_024 + round as u64 + index as u64,
|
|
256 + round as u64 + index as u64,
|
|
u64::from(index % 3 == 0),
|
|
u64::from(index % 3 == 0),
|
|
index % 3 == 0,
|
|
Some(2 + ((round + index) % 6) as u32),
|
|
)
|
|
.expect("pressure tick should keep bt runtime visible");
|
|
|
|
let tell_status = dispatcher.dispatch_json(rpc_request(
|
|
RpcMethod::Aria2TellStatus,
|
|
vec![RpcValue::String(gid.clone())],
|
|
));
|
|
match tell_status.result {
|
|
Some(RpcValue::Object(payload)) => {
|
|
assert_eq!(payload.get("isBt"), Some(&RpcValue::Bool(true)));
|
|
assert!(payload.contains_key("announceList"));
|
|
assert!(payload.contains_key("bitfield"));
|
|
assert!(payload.contains_key("shareRatio"));
|
|
assert!(payload.contains_key("shareTime"));
|
|
assert!(payload.contains_key("files"));
|
|
assert!(payload.contains_key("seeder"));
|
|
}
|
|
other => panic!("unexpected tellStatus payload in bt visibility bench: {other:?}"),
|
|
}
|
|
calls += 1;
|
|
|
|
let get_files = dispatcher.dispatch_json(rpc_request(
|
|
RpcMethod::Aria2GetFiles,
|
|
vec![RpcValue::String(gid.clone())],
|
|
));
|
|
match get_files.result {
|
|
Some(RpcValue::Array(entries)) => match entries.first() {
|
|
Some(RpcValue::Object(file)) => {
|
|
assert!(file.contains_key("selected"));
|
|
assert!(file.contains_key("completedLength"));
|
|
assert!(file.contains_key("bitfield"));
|
|
}
|
|
other => {
|
|
panic!("unexpected getFiles payload in bt visibility bench: {other:?}")
|
|
}
|
|
},
|
|
other => panic!("unexpected getFiles result in bt visibility bench: {other:?}"),
|
|
}
|
|
calls += 1;
|
|
|
|
let get_peers = dispatcher.dispatch_json(rpc_request(
|
|
RpcMethod::Aria2GetPeers,
|
|
vec![RpcValue::String(gid.clone())],
|
|
));
|
|
match get_peers.result {
|
|
Some(RpcValue::Array(entries)) => match entries.first() {
|
|
Some(RpcValue::Object(peer)) => {
|
|
assert!(peer.contains_key("ip"));
|
|
assert!(peer.contains_key("port"));
|
|
assert!(peer.contains_key("peerChoking"));
|
|
}
|
|
other => {
|
|
panic!("unexpected getPeers payload in bt visibility bench: {other:?}")
|
|
}
|
|
},
|
|
other => panic!("unexpected getPeers result in bt visibility bench: {other:?}"),
|
|
}
|
|
calls += 1;
|
|
}
|
|
}
|
|
calls
|
|
}
|
|
|
|
pub(super) fn bench_bt_visibility_pressure(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("bt_visibility_pressure");
|
|
for scenario in [
|
|
BtVisibilityPressureScenario {
|
|
task_count: 32,
|
|
rounds: 4,
|
|
},
|
|
BtVisibilityPressureScenario {
|
|
task_count: 64,
|
|
rounds: 4,
|
|
},
|
|
] {
|
|
group.throughput(Throughput::Elements(
|
|
(scenario.task_count * scenario.rounds * 3) as u64,
|
|
));
|
|
group.bench_with_input(
|
|
BenchmarkId::new("bt_visibility", scenario.task_count),
|
|
&scenario,
|
|
|b, &scenario| {
|
|
b.iter_batched(
|
|
|| seed_bt_visibility_dispatcher(scenario),
|
|
|(mut dispatcher, gids)| {
|
|
let calls = run_bt_visibility_pressure(&mut dispatcher, &gids, scenario);
|
|
assert_eq!(calls, scenario.task_count * scenario.rounds * 3);
|
|
},
|
|
criterion::BatchSize::SmallInput,
|
|
);
|
|
},
|
|
);
|
|
}
|
|
group.finish();
|
|
}
|