44// option. This file may not be copied, modified, or distributed
55// except according to those terms.
66
7- use std:: time:: Duration ;
7+ #![ expect(
8+ dead_code,
9+ reason = "Included by two bench binaries; each uses only one entry point."
10+ ) ]
811
9- use criterion:: { BenchmarkGroup , Criterion } ;
12+ use std:: { hint:: black_box, time:: Duration } ;
13+
14+ use criterion:: { Criterion , Throughput } ;
1015use test_fixture:: {
1116 boxed, fixture_init,
1217 sim:: {
@@ -19,34 +24,105 @@ use test_fixture::{
1924const RTT : Duration = Duration :: from_millis ( 10 ) ;
2025
2126/// Benchmark parameters: `(streams, data_size)`.
27+ ///
28+ /// Data sizes are well below the 1 MB per-stream flow-control window, so these
29+ /// benchmarks measure raw throughput and scheduling overhead without any
30+ /// flow-control blocking.
2231const BENCHMARK_PARAMS : [ ( usize , usize ) ; 3 ] = [ ( 1 , 1_000 ) , ( 1_000 , 1 ) , ( 1_000 , 1_000 ) ] ;
2332
24- /// Creates a ready simulator for benchmarking HTTP/3 streams.
25- pub fn setup ( streams : usize , data_size : usize ) -> ReadySimulator {
33+ /// Flow-control benchmark parameters: `(streams, data_size)`.
34+ ///
35+ /// Data sizes meet or exceed the 1 MB per-stream and 2 MB connection-level flow-control
36+ /// windows, so streams regularly block waiting for `MAX_STREAM_DATA` grants.
37+ const FC_BENCHMARK_PARAMS : [ ( usize , usize ) ; 2 ] = [
38+ ( 1 , 4 * 1024 * 1024 ) , // 4× per-stream window
39+ ( 10 , 1 * 1024 * 1024 ) , // each stream hits per-stream window; 5× connection-level window
40+ ] ;
41+
42+ fn setup_with_link (
43+ streams : usize ,
44+ data_size : usize ,
45+ link : impl Fn ( ) -> TailDrop ,
46+ ) -> ReadySimulator {
2647 let nodes = boxed ! [
2748 Node :: default_client( boxed![ Requests :: new( streams, data_size) ] ) ,
28- TailDrop :: dsl_uplink ( ) ,
49+ link ( ) ,
2950 Delay :: new( RTT ) ,
3051 Node :: default_server( boxed![ Responses :: new( streams, data_size) ] ) ,
31- TailDrop :: dsl_uplink ( ) ,
52+ link ( ) ,
3253 Delay :: new( RTT ) ,
3354 ] ;
3455 Simulator :: new ( "" , nodes) . setup ( )
3556}
3657
37- /// Runs benchmarks for all parameter combinations.
58+ /// Creates a ready simulator over a DSL-like link.
59+ ///
60+ /// The DSL uplink (200 KB/s, 60 ms one-way delay) keeps the bandwidth-delay
61+ /// product well below the default flow-control window, so no FC blocking occurs.
62+ pub fn setup ( streams : usize , data_size : usize ) -> ReadySimulator {
63+ setup_with_link ( streams, data_size, TailDrop :: dsl_uplink)
64+ }
65+
66+ /// Creates a ready simulator over a fast link where flow-control blocking occurs.
3867///
39- /// The closure receives the benchmark group and parameters, allowing each
40- /// benchmark to define its own measurement approach.
41- pub fn benchmark < M > ( c : & mut Criterion , mut measure : M )
42- where
43- M : FnMut ( & mut BenchmarkGroup < ' _ , criterion:: measurement:: WallTime > , usize , usize ) ,
44- {
68+ /// At 100 MB/s with a 20 ms RTT the bandwidth-delay product (~2 MB) exceeds the
69+ /// 1 MB per-stream flow-control window, so streams regularly exhaust their window
70+ /// and block waiting for `MAX_STREAM_DATA`.
71+ pub fn setup_flow_controlled ( streams : usize , data_size : usize ) -> ReadySimulator {
72+ // Link delay is zero so propagation RTT comes entirely from the Delay nodes
73+ // (10 ms each way = 20 ms RTT).
74+ setup_with_link ( streams, data_size, || {
75+ TailDrop :: new ( 100_000_000 , 2_000_000 , false , Duration :: ZERO )
76+ } )
77+ }
78+
79+ type SetupFn = fn ( usize , usize ) -> ReadySimulator ;
80+
81+ /// All benchmark configurations: `(criterion group, setup fn, params)`.
82+ const CONFIGS : [ ( & str , SetupFn , & [ ( usize , usize ) ] ) ; 2 ] = [
83+ ( "streams" , setup, & BENCHMARK_PARAMS ) ,
84+ (
85+ "streams-flow-controlled" ,
86+ setup_flow_controlled,
87+ & FC_BENCHMARK_PARAMS ,
88+ ) ,
89+ ] ;
90+
91+ /// Runs all stream benchmarks measuring wall-clock CPU time.
92+ pub fn walltime ( c : & mut Criterion ) {
4593 fixture_init ( ) ;
94+ for ( group_name, setup_fn, params) in CONFIGS {
95+ let mut group = c. benchmark_group ( group_name) ;
96+ for & ( streams, data_size) in params {
97+ let name = format ! ( "walltime/{streams}-streams/each-{data_size}-bytes" ) ;
98+ group. bench_function ( & name, |b| {
99+ b. iter_batched (
100+ || setup_fn ( streams, data_size) ,
101+ |sim| black_box ( sim. run ( ) ) ,
102+ criterion:: BatchSize :: SmallInput ,
103+ ) ;
104+ } ) ;
105+ }
106+ group. finish ( ) ;
107+ }
108+ }
46109
47- let mut group = c. benchmark_group ( "streams" ) ;
48- for ( streams, data_size) in BENCHMARK_PARAMS {
49- measure ( & mut group, streams, data_size) ;
110+ /// Runs all stream benchmarks measuring simulated network time (throughput).
111+ pub fn simulated ( c : & mut Criterion ) {
112+ fixture_init ( ) ;
113+ for ( group_name, setup_fn, params) in CONFIGS {
114+ let mut group = c. benchmark_group ( group_name) ;
115+ for & ( streams, data_size) in params {
116+ let name = format ! ( "simulated/{streams}-streams/each-{data_size}-bytes" ) ;
117+ group. throughput ( Throughput :: Bytes ( ( streams * data_size) as u64 ) ) ;
118+ group. bench_function ( & name, |b| {
119+ b. iter_custom ( |iters| {
120+ ( 0 ..iters)
121+ . map ( |_| setup_fn ( streams, data_size) . run ( ) )
122+ . sum :: < Duration > ( )
123+ } ) ;
124+ } ) ;
125+ }
126+ group. finish ( ) ;
50127 }
51- group. finish ( ) ;
52128}
0 commit comments