webgraph_cli/bench/
bvgraph.rs1use crate::GlobalArgs;
9use anyhow::Result;
10use clap::Parser;
11use dsi_bitstream::dispatch::factory::CodesReaderFactoryHelper;
12use dsi_bitstream::prelude::*;
13use itertools::Itertools;
14use lender::*;
15use rand::rngs::SmallRng;
16use rand::Rng;
17use rand::SeedableRng;
18use std::hint::black_box;
19use std::path::PathBuf;
20use webgraph::prelude::*;
21
22#[derive(Parser, Debug)]
23#[command(name = "bvgraph", about = "Benchmarks the Rust BvGraph implementation.", long_about = None)]
24pub struct CliArgs {
25 pub src: PathBuf,
27
28 #[arg(short, long)]
30 pub random: Option<usize>,
31
32 #[arg(short = 'R', long, default_value = "10")]
34 pub repeats: usize,
35
36 #[arg(short = 'f', long)]
38 pub first: bool,
39
40 #[arg(short = 'S', long = "static")]
42 pub _static: bool,
43
44 #[arg(short = 'd', long)]
46 pub degrees: bool,
47
48 #[arg(short = 'c', long)]
50 pub check: bool,
51
52 #[arg(long)]
55 pub slice: bool,
56}
57
58pub fn main(_global_args: GlobalArgs, args: CliArgs) -> Result<()> {
59 match get_endianness(&args.src)?.as_str() {
60 #[cfg(feature = "be_bins")]
61 BE::NAME => match args._static {
62 true => bench_webgraph::<BE, Static>(args),
63 false => bench_webgraph::<BE, Dynamic>(args),
64 },
65 #[cfg(feature = "le_bins")]
66 LE::NAME => match args._static {
67 true => bench_webgraph::<LE, Static>(args),
68 false => bench_webgraph::<LE, Dynamic>(args),
69 },
70 e => panic!("Unknown endianness: {}", e),
71 }
72}
73
74fn bench_random(graph: impl RandomAccessGraph, samples: usize, repeats: usize, first: bool) {
75 for _ in 0..repeats {
77 let mut rng = SmallRng::seed_from_u64(0);
78 let mut c: u64 = 0;
79 let num_nodes = graph.num_nodes();
80 let start = std::time::Instant::now();
81 if first {
82 for _ in 0..samples {
83 black_box(
84 graph
85 .successors(rng.random_range(0..num_nodes))
86 .into_iter()
87 .next()
88 .unwrap_or(0),
89 );
90 c += 1;
91 }
92 } else {
93 for _ in 0..samples {
94 c += black_box(
95 graph
96 .successors(rng.random_range(0..num_nodes))
97 .into_iter()
98 .count() as u64,
99 );
100 }
101 }
102
103 println!(
104 "{}: {:>20} ns/arc",
105 if first { "First" } else { "Random" },
106 (start.elapsed().as_secs_f64() / c as f64) * 1e9
107 );
108 }
109}
110
111fn bench_seq(graph: impl SequentialGraph, repeats: usize) {
112 for _ in 0..repeats {
113 let mut c: u64 = 0;
114
115 let start = std::time::Instant::now();
116 let mut iter = graph.iter();
117 while let Some((_, succ)) = iter.next() {
118 c += succ.into_iter().count() as u64;
119 }
120 println!(
121 "Sequential:{:>20} ns/arc",
122 (start.elapsed().as_secs_f64() / c as f64) * 1e9
123 );
124
125 assert_eq!(c, graph.num_arcs_hint().unwrap());
126 }
127}
128
129fn bench_webgraph<E: Endianness, D: Dispatch>(args: CliArgs) -> Result<()>
130where
131 MmapHelper<u32>: CodesReaderFactoryHelper<E>,
132 for<'a> LoadModeCodesReader<'a, E, Mmap>: BitSeek,
133{
134 if args.check {
135 let graph = BvGraph::with_basename(&args.src).endianness::<E>().load()?;
136
137 let seq_graph = BvGraphSeq::with_basename(&args.src)
138 .endianness::<E>()
139 .load()?;
140
141 let mut deg_reader = seq_graph.offset_deg_iter();
142
143 for_![ (node, seq_succ) in seq_graph {
145 let succ = graph.successors(node);
146
147 assert_eq!(deg_reader.next_degree()?, seq_succ.len());
148 assert_eq!(succ.collect_vec(), seq_succ.collect_vec());
149 }];
150 } else if args.degrees {
151 let seq_graph = BvGraphSeq::with_basename(&args.src)
152 .endianness::<E>()
153 .load()?;
154
155 for _ in 0..args.repeats {
156 let mut deg_reader = seq_graph.offset_deg_iter();
157
158 let mut c: u64 = 0;
159 let start = std::time::Instant::now();
160 for _ in 0..seq_graph.num_nodes() {
161 c += black_box(deg_reader.next_degree()? as u64);
162 }
163 println!(
164 "Degrees Only:{:>20} ns/arc",
165 (start.elapsed().as_secs_f64() / c as f64) * 1e9
166 );
167
168 assert_eq!(c, seq_graph.num_arcs_hint().unwrap());
169 }
170 } else {
171 match (
172 args.random,
173 std::any::TypeId::of::<D>() == std::any::TypeId::of::<Dynamic>(),
174 ) {
175 (Some(samples), true) => {
176 if args.slice {
177 bench_random(
178 BvGraph::with_basename(&args.src)
179 .endianness::<E>()
180 .dispatch::<Dynamic>()
181 .mode::<Mmap>()
182 .flags(MemoryFlags::TRANSPARENT_HUGE_PAGES | MemoryFlags::RANDOM_ACCESS)
183 .load()?
184 .offsets_to_slice(),
185 samples,
186 args.repeats,
187 args.first,
188 );
189 } else {
190 bench_random(
191 BvGraph::with_basename(&args.src)
192 .endianness::<E>()
193 .dispatch::<Dynamic>()
194 .mode::<Mmap>()
195 .flags(MemoryFlags::TRANSPARENT_HUGE_PAGES | MemoryFlags::RANDOM_ACCESS)
196 .load()?,
197 samples,
198 args.repeats,
199 args.first,
200 );
201 }
202 }
203 (Some(samples), false) => {
204 if args.slice {
205 bench_random(
206 BvGraph::with_basename(&args.src)
207 .endianness::<E>()
208 .dispatch::<Static>()
209 .mode::<Mmap>()
210 .flags(MemoryFlags::TRANSPARENT_HUGE_PAGES | MemoryFlags::RANDOM_ACCESS)
211 .load()?
212 .offsets_to_slice(),
213 samples,
214 args.repeats,
215 args.first,
216 );
217 } else {
218 bench_random(
219 BvGraph::with_basename(&args.src)
220 .endianness::<E>()
221 .dispatch::<Static>()
222 .mode::<Mmap>()
223 .flags(MemoryFlags::TRANSPARENT_HUGE_PAGES | MemoryFlags::RANDOM_ACCESS)
224 .load()?,
225 samples,
226 args.repeats,
227 args.first,
228 );
229 }
230 }
231 (None, true) => {
232 bench_seq(
233 BvGraphSeq::with_basename(&args.src)
234 .endianness::<E>()
235 .dispatch::<Dynamic>()
236 .mode::<Mmap>()
237 .flags(MemoryFlags::TRANSPARENT_HUGE_PAGES | MemoryFlags::SEQUENTIAL)
238 .load()?,
239 args.repeats,
240 );
241 }
242 (None, false) => {
243 bench_seq(
244 BvGraphSeq::with_basename(&args.src)
245 .endianness::<E>()
246 .dispatch::<Static>()
247 .mode::<Mmap>()
248 .flags(MemoryFlags::TRANSPARENT_HUGE_PAGES | MemoryFlags::SEQUENTIAL)
249 .load()?,
250 args.repeats,
251 );
252 }
253 }
254 }
255 Ok(())
256}