-
-
Notifications
You must be signed in to change notification settings - Fork 344
/
Copy pathmain.rs
140 lines (126 loc) · 4.81 KB
/
main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! Currently this experiment fails as `locate(…)` can't actually find objects. My guess is that
//! it can't find objects in packs for some reason because it could find some objects there were
//! probably loose, but failed right away after a `git gc`.
//! Let's see if a unit test can reproduce this too, right now this functionality is entirely untested
//! I think.
use anyhow::anyhow;
use std::{
io::BufRead,
path::{Path, PathBuf},
time::Instant,
};
fn main() -> anyhow::Result<()> {
if atty::is(atty::Stream::Stdin) {
anyhow::bail!("Need object hashes on stdin, one per line");
}
let mut repo_git_dir = std::env::args()
.skip(1)
.next()
.ok_or_else(|| anyhow!("First argument is the .git directory to work in"))
.and_then(|p| {
let p = PathBuf::from(p).canonicalize()?;
if p.extension().unwrap_or_default() == "git" || p.file_name().unwrap_or_default() == ".git" {
Ok(p)
} else {
Err(anyhow!("Path '{}' needs to be a .git directory", p.display()))
}
})?;
let start = Instant::now();
let stdin = std::io::stdin();
let stdin = stdin.lock();
let hashes = stdin.lines().collect::<Result<Vec<String>, _>>()?;
let elapsed = start.elapsed();
println!("{} objects (collected in {:?}", hashes.len(), elapsed);
let start = Instant::now();
let bytes = do_git2(hashes.as_slice(), &repo_git_dir)?;
let elapsed = start.elapsed();
let objs_per_sec = |elapsed: std::time::Duration| hashes.len() as f32 / elapsed.as_secs_f32();
println!(
"libgit2: confirmed {} bytes in {:?} ({:0.0} objects/s)",
bytes,
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
let bytes = do_parallel_git2(hashes.as_slice(), &repo_git_dir)?;
let elapsed = start.elapsed();
println!(
"parallel libgit2: confirmed {} bytes in {:?} ({:0.0} objects/s)",
bytes,
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
repo_git_dir.push("objects");
let bytes = do_gitoxide(hashes.as_slice(), &repo_git_dir)?;
let elapsed = start.elapsed();
println!(
"gitoxide: confirmed {} bytes in {:?} ({:0.0} objects/s)",
bytes,
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
let bytes = do_parallel_gitoxide(hashes.as_slice(), &repo_git_dir)?;
let elapsed = start.elapsed();
println!(
"parallel gitoxide: confirmed {} bytes in {:?} ({:0.0} objects/s)",
bytes,
elapsed,
objs_per_sec(elapsed)
);
Ok(())
}
fn do_git2(hashes: &[String], git_dir: &Path) -> anyhow::Result<u64> {
git2::opts::strict_hash_verification(false);
let repo = git2::Repository::open(git_dir)?;
let odb = repo.odb()?;
let mut bytes = 0u64;
for hash in hashes {
let hash = git2::Oid::from_str(&hash)?;
let obj = odb.read(hash)?;
bytes += obj.len() as u64;
}
Ok(bytes)
}
fn do_parallel_git2(hashes: &[String], git_dir: &Path) -> anyhow::Result<u64> {
use rayon::prelude::*;
git2::opts::strict_hash_verification(false);
let bytes = std::sync::atomic::AtomicU64::default();
hashes.par_iter().try_for_each_init::<_, _, _, anyhow::Result<_>>(
|| git2::Repository::open(git_dir).expect("git directory is valid"),
|repo, hash| {
let odb = repo.odb()?;
let hash = git2::Oid::from_str(&hash)?;
let obj = odb.read(hash)?;
bytes.fetch_add(obj.len() as u64, std::sync::atomic::Ordering::Relaxed);
Ok(())
},
)?;
Ok(bytes.load(std::sync::atomic::Ordering::Acquire))
}
fn do_gitoxide(hashes: &[String], objects_dir: &Path) -> anyhow::Result<u64> {
let odb = git_odb::compound::Db::at(objects_dir)?;
let mut buf = Vec::new();
let mut bytes = 0u64;
for hash in hashes {
let hash = git_hash::owned::Digest::from_40_bytes_in_hex(hash.as_bytes())?;
let obj = odb.locate(hash.to_borrowed(), &mut buf)?.expect("object must exist");
bytes += obj.size() as u64;
}
Ok(bytes)
}
fn do_parallel_gitoxide(hashes: &[String], objects_dir: &Path) -> anyhow::Result<u64> {
use rayon::prelude::*;
let odb = git_odb::compound::Db::at(objects_dir)?;
let bytes = std::sync::atomic::AtomicU64::default();
hashes
.par_iter()
.try_for_each_init::<_, _, _, anyhow::Result<_>>(Vec::new, |mut buf, hash| {
let hash = git_hash::owned::Digest::from_40_bytes_in_hex(hash.as_bytes())?;
let obj = odb.locate(hash.to_borrowed(), &mut buf)?.expect("object must exist");
bytes.fetch_add(obj.size() as u64, std::sync::atomic::Ordering::Relaxed);
Ok(())
})?;
Ok(bytes.load(std::sync::atomic::Ordering::Acquire))
}