This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / knot2 / crates / knot-bench / benches / pack.rs
7.9 kB 259 lines
1use std::time::Duration; 2 3use divan::Bencher; 4use divan::counter::{BytesCount, ItemsCount}; 5use knot_bench::{ChurnCount, CommitCount, HistorySpec, PathCount, build_history}; 6use knot_git::{Filter, Haves, PackBudget, Repo, Wants}; 7use knot_pack::{ 8 HaveOids, PackLimits, ReceiveCommand, ReceiveGuard, RefDecision, WantOids, count_expanded, 9 local_pack, receive_pack_guarded, upload_archive, write_expanded, write_pack, 10}; 11use knot_types::{ObjectCount, Oid}; 12 13#[global_allocator] 14static ALLOC: divan::AllocProfiler = divan::AllocProfiler::system(); 15 16const GRADES: &[u32] = &[64, 256, 1024]; 17const CAP: u64 = 512 * 1024 * 1024; 18 19fn spec_for(commits: u32) -> HistorySpec { 20 HistorySpec { 21 commits: CommitCount::new(commits), 22 paths: PathCount::new(commits.max(64)), 23 churn: ChurnCount::new(8), 24 } 25} 26 27fn pkt(payload: &[u8]) -> Vec<u8> { 28 let mut out = format!("{:04x}", payload.len() + 4).into_bytes(); 29 out.extend_from_slice(payload); 30 out 31} 32 33struct AllowAll; 34 35impl ReceiveGuard for AllowAll { 36 fn authorize(&self, _staged: &Repo, commands: &[ReceiveCommand]) -> Vec<RefDecision> { 37 commands.iter().map(|_| RefDecision::Allow).collect() 38 } 39} 40 41fn main() { 42 divan::main(); 43} 44 45#[divan::bench(args = GRADES)] 46fn select(bencher: Bencher, commits: u32) { 47 let history = build_history(spec_for(commits)); 48 let tips = history.tips(); 49 let count = history 50 .repo() 51 .select_pack_objects_filtered( 52 Wants::new(&tips), 53 Haves::new(&[]), 54 Filter::None, 55 PackBudget::unbounded(), 56 ) 57 .unwrap() 58 .send 59 .len(); 60 bencher.counter(ItemsCount::new(count)).bench_local(|| { 61 history 62 .repo() 63 .select_pack_objects_filtered( 64 Wants::new(&tips), 65 Haves::new(&[]), 66 Filter::None, 67 PackBudget::unbounded(), 68 ) 69 .unwrap() 70 }); 71} 72 73#[divan::bench(args = GRADES)] 74fn clone(bencher: Bencher, commits: u32) { 75 let history = build_history(spec_for(commits)); 76 let wants = WantOids::new(history.tips()); 77 let no_haves = HaveOids::default(); 78 let bytes = local_pack(history.repo(), &wants, &no_haves, CAP) 79 .unwrap() 80 .len(); 81 bencher 82 .counter(BytesCount::new(bytes)) 83 .bench_local(|| local_pack(history.repo(), &wants, &no_haves, CAP).unwrap()); 84} 85 86#[divan::bench(args = GRADES)] 87fn full_clone_manual(bencher: Bencher, commits: u32) { 88 let history = build_history(spec_for(commits)); 89 let tips = history.tips(); 90 let dir = history.repo().objects_dir(); 91 let count = history 92 .repo() 93 .select_pack_objects_filtered( 94 Wants::new(&tips), 95 Haves::new(&[]), 96 Filter::None, 97 PackBudget::unbounded(), 98 ) 99 .unwrap() 100 .send 101 .len(); 102 bencher.counter(ItemsCount::new(count)).bench_local(|| { 103 let send = history 104 .repo() 105 .select_pack_objects_filtered( 106 Wants::new(&tips), 107 Haves::new(&[]), 108 Filter::None, 109 PackBudget::unbounded(), 110 ) 111 .unwrap() 112 .send; 113 write_pack( 114 &dir, 115 send, 116 None, 117 &mut std::io::sink(), 118 history.repo().object_format().kind(), 119 ) 120 .unwrap(); 121 }); 122} 123 124#[divan::bench(args = GRADES)] 125fn full_clone_expanding(bencher: Bencher, commits: u32) { 126 let history = build_history(spec_for(commits)); 127 let tips = history.tips(); 128 let dir = history.repo().objects_dir(); 129 let far = Duration::from_secs(3600); 130 let roots = history 131 .repo() 132 .clone_roots(&tips, PackBudget::unbounded()) 133 .unwrap(); 134 let kind = history.repo().object_format().kind(); 135 let count = count_expanded(&dir, roots, ObjectCount::new(usize::MAX), far, kind) 136 .unwrap() 137 .len(); 138 bencher.counter(ItemsCount::new(count)).bench_local(|| { 139 let roots = history 140 .repo() 141 .clone_roots(&tips, PackBudget::unbounded()) 142 .unwrap(); 143 let pack = count_expanded(&dir, roots, ObjectCount::new(usize::MAX), far, kind).unwrap(); 144 write_expanded(pack, &mut std::io::sink()).unwrap(); 145 }); 146} 147 148#[divan::bench(args = GRADES)] 149fn fetch(bencher: Bencher, commits: u32) { 150 let history = build_history(spec_for(commits)); 151 let tips = history.tips(); 152 let walk = history 153 .repo() 154 .rev_walk(Wants::new(&tips), Haves::new(&[])) 155 .unwrap(); 156 let haves = HaveOids::new(vec![walk[walk.len() / 2]]); 157 let wants = WantOids::new(tips); 158 let bytes = local_pack(history.repo(), &wants, &haves, CAP) 159 .unwrap() 160 .len(); 161 bencher 162 .counter(BytesCount::new(bytes)) 163 .bench_local(|| local_pack(history.repo(), &wants, &haves, CAP).unwrap()); 164} 165 166#[divan::bench(args = GRADES)] 167fn push(bencher: Bencher, commits: u32) { 168 let history = build_history(spec_for(commits)); 169 let tips = history.tips(); 170 let pack = local_pack( 171 history.repo(), 172 &WantOids::new(tips.clone()), 173 &HaveOids::default(), 174 CAP, 175 ) 176 .unwrap(); 177 let walk = history 178 .repo() 179 .rev_walk(Wants::new(&tips), Haves::new(&[])) 180 .unwrap(); 181 let stride = walk.len().max(1) / 8 + 1; 182 let branch_tips: Vec<Oid> = walk.iter().step_by(stride).copied().collect(); 183 let request = build_receive_request(&branch_tips, &pack); 184 let limits = PackLimits::default(); 185 bencher 186 .counter(ItemsCount::new(branch_tips.len())) 187 .with_inputs(fresh_target) 188 .bench_local_values(|target| { 189 receive_pack_guarded( 190 target.repo(), 191 &request, 192 &limits, 193 &AllowAll, 194 &|_| {}, 195 &knot_pack::default_catalog().reject, 196 ) 197 .unwrap() 198 }); 199} 200 201#[divan::bench(args = GRADES)] 202fn archive(bencher: Bencher, commits: u32) { 203 let history = build_history(spec_for(commits)); 204 let request = build_archive_request(history.tip()); 205 let bytes = upload_archive(history.repo(), &request, knot_git::ArchiveLimit::default()) 206 .unwrap() 207 .len(); 208 bencher.counter(BytesCount::new(bytes)).bench_local(|| { 209 upload_archive(history.repo(), &request, knot_git::ArchiveLimit::default()).unwrap() 210 }); 211} 212 213struct FreshTarget { 214 _dir: tempfile::TempDir, 215 repo: Repo, 216} 217 218impl FreshTarget { 219 fn repo(&self) -> &Repo { 220 &self.repo 221 } 222} 223 224fn fresh_target() -> FreshTarget { 225 let dir = tempfile::tempdir().expect("tempdir"); 226 let repo = Repo::create(dir.path().join("target.git")).expect("create target"); 227 FreshTarget { _dir: dir, repo } 228} 229 230fn build_receive_request(branch_tips: &[Oid], pack: &[u8]) -> Vec<u8> { 231 let null = Oid::null().to_hex(); 232 let commands: Vec<Vec<u8>> = branch_tips 233 .iter() 234 .enumerate() 235 .map(|(index, tip)| { 236 let line = format!("{null} {} refs/heads/b{index}", tip.to_hex()); 237 match index { 238 0 => { 239 let mut payload = line.into_bytes(); 240 payload.push(0); 241 payload.extend_from_slice(b"report-status\n"); 242 pkt(&payload) 243 } 244 _ => pkt(format!("{line}\n").as_bytes()), 245 } 246 }) 247 .collect(); 248 let mut request: Vec<u8> = commands.concat(); 249 request.extend_from_slice(b"0000"); 250 request.extend_from_slice(pack); 251 request 252} 253 254fn build_archive_request(tip: Oid) -> Vec<u8> { 255 let mut request = pkt(b"argument --format=tar"); 256 request.extend_from_slice(&pkt(format!("argument {}", tip.to_hex()).as_bytes())); 257 request.extend_from_slice(b"0000"); 258 request 259}