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::RngExt;
16use rand::SeedableRng;
17use rand::rngs::SmallRng;
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 basename: 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.basename)?.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.basename)
136 .endianness::<E>()
137 .load()?;
138
139 let seq_graph = BvGraphSeq::with_basename(&args.basename)
140 .endianness::<E>()
141 .load()?;
142
143 let mut deg_reader = seq_graph.offset_deg_iter();
144
145 for_![ (node, seq_succ) in seq_graph {
147 let succ = graph.successors(node);
148
149 assert_eq!(deg_reader.next_degree()?, seq_succ.len());
150 assert_eq!(succ.collect_vec(), seq_succ.collect_vec());
151 }];
152 } else if args.degrees {
153 let seq_graph = BvGraphSeq::with_basename(&args.basename)
154 .endianness::<E>()
155 .load()?;
156
157 for _ in 0..args.repeats {
158 let mut deg_reader = seq_graph.offset_deg_iter();
159
160 let mut c: u64 = 0;
161 let start = std::time::Instant::now();
162 for _ in 0..seq_graph.num_nodes() {
163 c += black_box(deg_reader.next_degree()? as u64);
164 }
165 println!(
166 "Degrees Only:{:>20} ns/arc",
167 (start.elapsed().as_secs_f64() / c as f64) * 1e9
168 );
169
170 assert_eq!(c, seq_graph.num_arcs_hint().unwrap());
171 }
172 } else {
173 match (
174 args.random,
175 std::any::TypeId::of::<D>() == std::any::TypeId::of::<Dynamic>(),
176 ) {
177 (Some(samples), true) => {
178 if args.slice {
179 bench_random(
180 BvGraph::with_basename(&args.basename)
181 .endianness::<E>()
182 .dispatch::<Dynamic>()
183 .mode::<Mmap>()
184 .flags(MemoryFlags::TRANSPARENT_HUGE_PAGES | MemoryFlags::RANDOM_ACCESS)
185 .load()?
186 .offsets_to_slice(),
187 samples,
188 args.repeats,
189 args.first,
190 );
191 } else {
192 bench_random(
193 BvGraph::with_basename(&args.basename)
194 .endianness::<E>()
195 .dispatch::<Dynamic>()
196 .mode::<Mmap>()
197 .flags(MemoryFlags::TRANSPARENT_HUGE_PAGES | MemoryFlags::RANDOM_ACCESS)
198 .load()?,
199 samples,
200 args.repeats,
201 args.first,
202 );
203 }
204 }
205 (Some(samples), false) => {
206 if args.slice {
207 bench_random(
208 BvGraph::with_basename(&args.basename)
209 .endianness::<E>()
210 .dispatch::<Static>()
211 .mode::<Mmap>()
212 .flags(MemoryFlags::TRANSPARENT_HUGE_PAGES | MemoryFlags::RANDOM_ACCESS)
213 .load()?
214 .offsets_to_slice(),
215 samples,
216 args.repeats,
217 args.first,
218 );
219 } else {
220 bench_random(
221 BvGraph::with_basename(&args.basename)
222 .endianness::<E>()
223 .dispatch::<Static>()
224 .mode::<Mmap>()
225 .flags(MemoryFlags::TRANSPARENT_HUGE_PAGES | MemoryFlags::RANDOM_ACCESS)
226 .load()?,
227 samples,
228 args.repeats,
229 args.first,
230 );
231 }
232 }
233 (None, true) => {
234 bench_seq(
235 BvGraphSeq::with_basename(&args.basename)
236 .endianness::<E>()
237 .dispatch::<Dynamic>()
238 .mode::<Mmap>()
239 .flags(MemoryFlags::TRANSPARENT_HUGE_PAGES | MemoryFlags::SEQUENTIAL)
240 .load()?,
241 args.repeats,
242 );
243 }
244 (None, false) => {
245 bench_seq(
246 BvGraphSeq::with_basename(&args.basename)
247 .endianness::<E>()
248 .dispatch::<Static>()
249 .mode::<Mmap>()
250 .flags(MemoryFlags::TRANSPARENT_HUGE_PAGES | MemoryFlags::SEQUENTIAL)
251 .load()?,
252 args.repeats,
253 );
254 }
255 }
256 }
257 Ok(())
258}