use std::{path::Path, sync::Arc, time::Instant}; use anyhow::anyhow;use git_repository::{hash::ObjectId, odb, threading::OwnShared, ThreadSafeRepository}; use crate::odb::Cache; const GITOXIDE_STATIC_CACHE_SIZE: usize = 64;const GITOXIDE_CACHED_OBJECT_DATA_PER_THREAD_IN_BYTES: usize = 60_000_000; fn main() -> anyhow::Result<()> { let repo_git_dir = std::env::args() .nth(1) .ok_or_else(|| anyhow!("First argument is the .git directory to work in"))?; let repo = git_repository::discover(&repo_git_dir)?.into_sync(); let hashes = { let start = Instant::now(); let repo = git_repository::discover(repo_git_dir)?; let hashes = repo.objects.iter()?.collect::<Result<Vec<_>, _>>()?; let elapsed = start.elapsed(); println!("gitoxide: {} objects (collected in {:?}", hashes.len(), elapsed); hashes }; let objs_per_sec = |elapsed: std::time::Duration| hashes.len() as f32 / elapsed.as_secs_f32(); let start = Instant::now(); do_gitoxide_in_parallel_sync(&hashes, &repo, || odb::pack::cache::Never, AccessMode::ObjectExists)?; let elapsed = start.elapsed(); println!( "parallel gitoxide : confirmed {} objects exists in {:?} ({:0.0} objects/s)", hashes.len(), elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); let (object_store, _) = do_new_gitoxide_store_in_parallel( &hashes, repo.objects_dir(), || odb::pack::cache::Never, AccessMode::ObjectExists, None, )?; let elapsed = start.elapsed(); println!( "parallel gitoxide (new store): confirmed {} objects exists in {:?} ({:0.0} objects/s)", hashes.len(), elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); do_new_gitoxide_store_in_parallel( &hashes, repo.objects_dir(), || odb::pack::cache::Never, AccessMode::ObjectExists, Some(object_store), )?; let elapsed = start.elapsed(); println!( "parallel gitoxide (new store, warm): confirmed {} objects exists in {:?} ({:0.0} objects/s)", hashes.len(), elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); do_gitoxide_in_parallel_through_arc( &hashes, repo.objects_dir(), odb::pack::cache::Never::default, AccessMode::ObjectExists, )?; let elapsed = start.elapsed(); println!( "parallel gitoxide (Arc): confirmed {} objects exists in {:?} ({:0.0} objects/s)", hashes.len(), elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); do_parallel_git2(hashes.as_slice(), repo.git_dir(), AccessMode::ObjectExists)?; let elapsed = start.elapsed(); println!( "parallel libgit2: confirmed {} exist in {:?} ({:0.0} objects/s)", hashes.len(), elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); do_git2(hashes.as_slice(), repo.git_dir(), AccessMode::ObjectExists)?; let elapsed = start.elapsed(); println!( "libgit2: confirmed {} exist in {:?} ({:0.0} objects/s)", hashes.len(), elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); let (object_store, bytes) = do_new_gitoxide_store_in_parallel( &hashes, repo.objects_dir(), || odb::pack::cache::Never, AccessMode::ObjectData, None, )?; let elapsed = start.elapsed(); println!( "parallel gitoxide (new store, uncached): confirmed {} bytes exists in {:?} ({:0.0} objects/s)", bytes, elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); let (object_store, bytes) = do_new_gitoxide_store_in_parallel( &hashes, repo.objects_dir(), || odb::pack::cache::Never, AccessMode::ObjectData, Some(object_store), )?; let elapsed = start.elapsed(); println!( "parallel gitoxide (new store, uncached, warm): confirmed {} bytes exists in {:?} ({:0.0} objects/s)", bytes, elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); let (_, bytes) = do_new_gitoxide_store_in_parallel( &hashes, repo.objects_dir(), || odb::pack::cache::lru::MemoryCappedHashmap::new(GITOXIDE_CACHED_OBJECT_DATA_PER_THREAD_IN_BYTES), AccessMode::ObjectData, Some(object_store), )?; let elapsed = start.elapsed(); println!( "parallel gitoxide (new store, cache = {:.0}MB), warm): confirmed {} bytes exists in {:?} ({:0.0} objects/s)", GITOXIDE_CACHED_OBJECT_DATA_PER_THREAD_IN_BYTES as f32 / (1024 * 1024) as f32, bytes, elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); let bytes = do_gitoxide_in_parallel_sync(&hashes, &repo, odb::pack::cache::Never::default, AccessMode::ObjectData)?; let elapsed = start.elapsed(); println!( "parallel gitoxide (uncached): confirmed {} bytes in {:?} ({:0.0} objects/s)", bytes, elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); let bytes = do_gitoxide_in_parallel_through_arc( &hashes, repo.objects_dir(), odb::pack::cache::Never::default, AccessMode::ObjectData, )?; let elapsed = start.elapsed(); println!( "parallel gitoxide (uncached, Arc): confirmed {} bytes in {:?} ({:0.0} objects/s)", bytes, elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); let bytes = do_gitoxide_in_parallel_sync( &hashes, &repo, || odb::pack::cache::lru::MemoryCappedHashmap::new(GITOXIDE_CACHED_OBJECT_DATA_PER_THREAD_IN_BYTES), AccessMode::ObjectData, )?; let elapsed = start.elapsed(); println!( "parallel gitoxide (cache = {:.0}MB): confirmed {} bytes in {:?} ({:0.0} objects/s)", GITOXIDE_CACHED_OBJECT_DATA_PER_THREAD_IN_BYTES as f32 / (1024 * 1024) as f32, bytes, elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); let bytes = do_gitoxide_in_parallel_sync( &hashes, &repo, odb::pack::cache::lru::StaticLinkedList::<GITOXIDE_STATIC_CACHE_SIZE>::default, AccessMode::ObjectData, )?; let elapsed = start.elapsed(); println!( "parallel gitoxide (small static cache): confirmed {} bytes in {:?} ({:0.0} objects/s)", bytes, elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); let bytes = do_git2(hashes.as_slice(), repo.git_dir(), AccessMode::ObjectData)?; let elapsed = start.elapsed(); println!( "libgit2: confirmed {} bytes in {:?} ({:0.0} objects/s)", bytes, elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); let bytes = do_gitoxide(&hashes, &repo, || { odb::pack::cache::lru::MemoryCappedHashmap::new(GITOXIDE_CACHED_OBJECT_DATA_PER_THREAD_IN_BYTES) })?; let elapsed = start.elapsed(); let objs_per_sec = |elapsed: std::time::Duration| hashes.len() as f32 / elapsed.as_secs_f32(); println!( "gitoxide (cache = {:.0}MB): confirmed {} bytes in {:?} ({:0.0} objects/s)", GITOXIDE_CACHED_OBJECT_DATA_PER_THREAD_IN_BYTES as f32 / (1024 * 1024) as f32, bytes, elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); let bytes = do_gitoxide( &hashes, &repo, odb::pack::cache::lru::StaticLinkedList::<GITOXIDE_STATIC_CACHE_SIZE>::default, )?; let elapsed = start.elapsed(); println!( "gitoxide (small static cache): confirmed {} bytes in {:?} ({:0.0} objects/s)", bytes, elapsed, objs_per_sec(elapsed) ); let start = Instant::now(); let bytes = do_gitoxide(&hashes, &repo, odb::pack::cache::Never::default)?; let elapsed = start.elapsed(); println!( "gitoxide (uncached): 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(), AccessMode::ObjectData)?; let elapsed = start.elapsed(); println!( "parallel libgit2: confirmed {} bytes in {:?} ({:0.0} objects/s)", bytes, elapsed, objs_per_sec(elapsed) ); Ok(())} fn do_git2(hashes: &[ObjectId], git_dir: &Path, mode: AccessMode) -> 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_bytes(hash.as_slice())?; match mode { AccessMode::ObjectData => { let obj = odb.read(hash)?; bytes += obj.len() as u64; } AccessMode::ObjectExists => { assert!(odb.exists(hash)); } } } Ok(bytes)} fn do_parallel_git2(hashes: &[ObjectId], git_dir: &Path, mode: AccessMode) -> 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_bytes(hash.as_slice())?; match mode { AccessMode::ObjectData => { let obj = odb.read(hash)?; bytes.fetch_add(obj.len() as u64, std::sync::atomic::Ordering::Relaxed); } AccessMode::ObjectExists => { assert!(odb.exists(hash)); } } Ok(()) }, )?; Ok(bytes.load(std::sync::atomic::Ordering::Acquire))} fn do_gitoxide<C>( hashes: &[ObjectId], repo: &ThreadSafeRepository, new_cache: impl Fn() -> C + Send + Sync + 'static,) -> anyhow::Result<u64>where C: odb::pack::cache::DecodeEntry + Send + 'static,{ use git_repository::prelude::FindExt; let mut buf = Vec::new(); let mut bytes = 0u64; let handle = repo.objects.to_cache().with_pack_cache(move || Box::new(new_cache())); for hash in hashes { let obj = handle.find(hash, &mut buf)?; bytes += obj.data.len() as u64; } Ok(bytes)} enum AccessMode { ObjectData, ObjectExists,} fn do_gitoxide_in_parallel_sync<C>( hashes: &[ObjectId], repo: &ThreadSafeRepository, new_cache: impl Fn() -> C + Send + Clone + Sync + 'static, mode: AccessMode,) -> anyhow::Result<u64>where C: odb::pack::cache::DecodeEntry + Send + 'static,{ use git_repository::prelude::FindExt; use rayon::prelude::*; let bytes = std::sync::atomic::AtomicU64::default(); hashes.par_iter().try_for_each_init::<_, _, _, anyhow::Result<_>>( move || { ( Vec::new(), repo.objects.to_cache().with_pack_cache({ let new_cache = new_cache.clone(); move || Box::new(new_cache()) }), ) }, |(buf, objects), hash| { match mode { AccessMode::ObjectData => { let obj = objects.find(hash, buf)?; bytes.fetch_add(obj.data.len() as u64, std::sync::atomic::Ordering::Relaxed); } AccessMode::ObjectExists => { use git_repository::prelude::Find; assert!(objects.contains(hash), "each traversed object exists"); } } Ok(()) }, )?; Ok(bytes.load(std::sync::atomic::Ordering::Acquire))} fn do_new_gitoxide_store_in_parallel<C>( hashes: &[ObjectId], objects_dir: &Path, new_cache: impl Fn() -> C + Send + Sync + 'static, mode: AccessMode, store: Option<OwnShared<odb::Store>>,) -> anyhow::Result<(std::sync::Arc<odb::Store>, u64)>where C: odb::pack::cache::DecodeEntry + Send + 'static,{ use git_repository::prelude::FindExt; let bytes = std::sync::atomic::AtomicU64::default(); let slots = std::env::args() .nth(2) .and_then(|num| num.parse().ok().map(odb::store::init::Slots::Given)) .unwrap_or_default(); let store = match store { Some(store) => store, None => OwnShared::new(odb::Store::at_opts( objects_dir, Vec::new(), git_repository::odb::store::init::Options { slots, ..Default::default() }, )?), }; let handle = Cache::from(store.to_handle()).with_pack_cache(move || Box::new(new_cache())); git_repository::parallel::in_parallel( hashes.chunks(1000), None, move |_| (Vec::new(), handle.clone()), |hashes, (buf, cache)| { for hash in hashes { match mode { AccessMode::ObjectData => { let obj = cache.find(hash, buf)?; bytes.fetch_add(obj.data.len() as u64, std::sync::atomic::Ordering::Relaxed); } AccessMode::ObjectExists => { use git_repository::prelude::Find; assert!(cache.contains(hash), "each traversed object exists"); } } } Ok(()) }, git_repository::parallel::reduce::IdentityWithResult::<(), anyhow::Error>::default(), )?; Ok((store, bytes.load(std::sync::atomic::Ordering::Acquire)))} fn do_gitoxide_in_parallel_through_arc<C>( hashes: &[ObjectId], objects_dir: &Path, new_cache: impl Fn() -> C + Send + Sync + Clone + 'static, mode: AccessMode,) -> anyhow::Result<u64>where C: odb::pack::cache::DecodeEntry + Send + 'static,{ use git_repository::prelude::FindExt; let bytes = std::sync::atomic::AtomicU64::default(); #[allow(deprecated)] let odb = Arc::new(odb::linked::Store::at(objects_dir)?); git_repository::parallel::in_parallel( hashes.chunks(1000), None, move |_| { ( Vec::new(), odb.to_cache_arc().with_pack_cache({ let new_cache = new_cache.clone(); move || Box::new(new_cache()) }), ) }, |hashes, (buf, odb)| { for hash in hashes { match mode { AccessMode::ObjectData => { let obj = odb.find(hash, buf)?; bytes.fetch_add(obj.data.len() as u64, std::sync::atomic::Ordering::Relaxed); } AccessMode::ObjectExists => { use git_repository::prelude::Find; assert!(odb.contains(hash), "each traversed object exists"); } } } Ok(()) }, git_repository::parallel::reduce::IdentityWithResult::<(), anyhow::Error>::default(), )?; Ok(bytes.load(std::sync::atomic::Ordering::Acquire))}
use std::{path::Path, sync::Arc, time::Instant};
use anyhow::anyhow;
use git_repository::{hash::ObjectId, odb, threading::OwnShared, ThreadSafeRepository};
use crate::odb::Cache;
const GITOXIDE_STATIC_CACHE_SIZE: usize = 64;
const GITOXIDE_CACHED_OBJECT_DATA_PER_THREAD_IN_BYTES: usize = 60_000_000;
fn main() -> anyhow::Result<()> {
let repo_git_dir = std::env::args()
.nth(1)
.ok_or_else(|| anyhow!("First argument is the .git directory to work in"))?;
let repo = git_repository::discover(&repo_git_dir)?.into_sync();
let hashes = {
let start = Instant::now();
let repo = git_repository::discover(repo_git_dir)?;
let hashes = repo.objects.iter()?.collect::<Result<Vec<_>, _>>()?;
let elapsed = start.elapsed();
println!("gitoxide: {} objects (collected in {:?}", hashes.len(), elapsed);
hashes
};
let objs_per_sec = |elapsed: std::time::Duration| hashes.len() as f32 / elapsed.as_secs_f32();
let start = Instant::now();
do_gitoxide_in_parallel_sync(&hashes, &repo, || odb::pack::cache::Never, AccessMode::ObjectExists)?;
let elapsed = start.elapsed();
println!(
"parallel gitoxide : confirmed {} objects exists in {:?} ({:0.0} objects/s)",
hashes.len(),
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
let (object_store, _) = do_new_gitoxide_store_in_parallel(
&hashes,
repo.objects_dir(),
|| odb::pack::cache::Never,
AccessMode::ObjectExists,
None,
)?;
let elapsed = start.elapsed();
println!(
"parallel gitoxide (new store): confirmed {} objects exists in {:?} ({:0.0} objects/s)",
hashes.len(),
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
do_new_gitoxide_store_in_parallel(
&hashes,
repo.objects_dir(),
|| odb::pack::cache::Never,
AccessMode::ObjectExists,
Some(object_store),
)?;
let elapsed = start.elapsed();
println!(
"parallel gitoxide (new store, warm): confirmed {} objects exists in {:?} ({:0.0} objects/s)",
hashes.len(),
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
do_gitoxide_in_parallel_through_arc(
&hashes,
repo.objects_dir(),
odb::pack::cache::Never::default,
AccessMode::ObjectExists,
)?;
let elapsed = start.elapsed();
println!(
"parallel gitoxide (Arc): confirmed {} objects exists in {:?} ({:0.0} objects/s)",
hashes.len(),
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
do_parallel_git2(hashes.as_slice(), repo.git_dir(), AccessMode::ObjectExists)?;
let elapsed = start.elapsed();
println!(
"parallel libgit2: confirmed {} exist in {:?} ({:0.0} objects/s)",
hashes.len(),
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
do_git2(hashes.as_slice(), repo.git_dir(), AccessMode::ObjectExists)?;
let elapsed = start.elapsed();
println!(
"libgit2: confirmed {} exist in {:?} ({:0.0} objects/s)",
hashes.len(),
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
let (object_store, bytes) = do_new_gitoxide_store_in_parallel(
&hashes,
repo.objects_dir(),
|| odb::pack::cache::Never,
AccessMode::ObjectData,
None,
)?;
let elapsed = start.elapsed();
println!(
"parallel gitoxide (new store, uncached): confirmed {} bytes exists in {:?} ({:0.0} objects/s)",
bytes,
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
let (object_store, bytes) = do_new_gitoxide_store_in_parallel(
&hashes,
repo.objects_dir(),
|| odb::pack::cache::Never,
AccessMode::ObjectData,
Some(object_store),
)?;
let elapsed = start.elapsed();
println!(
"parallel gitoxide (new store, uncached, warm): confirmed {} bytes exists in {:?} ({:0.0} objects/s)",
bytes,
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
let (_, bytes) = do_new_gitoxide_store_in_parallel(
&hashes,
repo.objects_dir(),
|| odb::pack::cache::lru::MemoryCappedHashmap::new(GITOXIDE_CACHED_OBJECT_DATA_PER_THREAD_IN_BYTES),
AccessMode::ObjectData,
Some(object_store),
)?;
let elapsed = start.elapsed();
println!(
"parallel gitoxide (new store, cache = {:.0}MB), warm): confirmed {} bytes exists in {:?} ({:0.0} objects/s)",
GITOXIDE_CACHED_OBJECT_DATA_PER_THREAD_IN_BYTES as f32 / (1024 * 1024) as f32,
bytes,
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
let bytes = do_gitoxide_in_parallel_sync(&hashes, &repo, odb::pack::cache::Never::default, AccessMode::ObjectData)?;
let elapsed = start.elapsed();
println!(
"parallel gitoxide (uncached): confirmed {} bytes in {:?} ({:0.0} objects/s)",
bytes,
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
let bytes = do_gitoxide_in_parallel_through_arc(
&hashes,
repo.objects_dir(),
odb::pack::cache::Never::default,
AccessMode::ObjectData,
)?;
let elapsed = start.elapsed();
println!(
"parallel gitoxide (uncached, Arc): confirmed {} bytes in {:?} ({:0.0} objects/s)",
bytes,
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
let bytes = do_gitoxide_in_parallel_sync(
&hashes,
&repo,
|| odb::pack::cache::lru::MemoryCappedHashmap::new(GITOXIDE_CACHED_OBJECT_DATA_PER_THREAD_IN_BYTES),
AccessMode::ObjectData,
)?;
let elapsed = start.elapsed();
println!(
"parallel gitoxide (cache = {:.0}MB): confirmed {} bytes in {:?} ({:0.0} objects/s)",
GITOXIDE_CACHED_OBJECT_DATA_PER_THREAD_IN_BYTES as f32 / (1024 * 1024) as f32,
bytes,
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
let bytes = do_gitoxide_in_parallel_sync(
&hashes,
&repo,
odb::pack::cache::lru::StaticLinkedList::<GITOXIDE_STATIC_CACHE_SIZE>::default,
AccessMode::ObjectData,
)?;
let elapsed = start.elapsed();
println!(
"parallel gitoxide (small static cache): confirmed {} bytes in {:?} ({:0.0} objects/s)",
bytes,
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
let bytes = do_git2(hashes.as_slice(), repo.git_dir(), AccessMode::ObjectData)?;
let elapsed = start.elapsed();
println!(
"libgit2: confirmed {} bytes in {:?} ({:0.0} objects/s)",
bytes,
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
let bytes = do_gitoxide(&hashes, &repo, || {
odb::pack::cache::lru::MemoryCappedHashmap::new(GITOXIDE_CACHED_OBJECT_DATA_PER_THREAD_IN_BYTES)
})?;
let elapsed = start.elapsed();
let objs_per_sec = |elapsed: std::time::Duration| hashes.len() as f32 / elapsed.as_secs_f32();
println!(
"gitoxide (cache = {:.0}MB): confirmed {} bytes in {:?} ({:0.0} objects/s)",
GITOXIDE_CACHED_OBJECT_DATA_PER_THREAD_IN_BYTES as f32 / (1024 * 1024) as f32,
bytes,
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
let bytes = do_gitoxide(
&hashes,
&repo,
odb::pack::cache::lru::StaticLinkedList::<GITOXIDE_STATIC_CACHE_SIZE>::default,
)?;
let elapsed = start.elapsed();
println!(
"gitoxide (small static cache): confirmed {} bytes in {:?} ({:0.0} objects/s)",
bytes,
elapsed,
objs_per_sec(elapsed)
);
let start = Instant::now();
let bytes = do_gitoxide(&hashes, &repo, odb::pack::cache::Never::default)?;
let elapsed = start.elapsed();
println!(
"gitoxide (uncached): 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(), AccessMode::ObjectData)?;
let elapsed = start.elapsed();
println!(
"parallel libgit2: confirmed {} bytes in {:?} ({:0.0} objects/s)",
bytes,
elapsed,
objs_per_sec(elapsed)
);
Ok(())
}
fn do_git2(hashes: &[ObjectId], git_dir: &Path, mode: AccessMode) -> 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_bytes(hash.as_slice())?;
match mode {
AccessMode::ObjectData => {
let obj = odb.read(hash)?;
bytes += obj.len() as u64;
}
AccessMode::ObjectExists => {
assert!(odb.exists(hash));
}
}
}
Ok(bytes)
}
fn do_parallel_git2(hashes: &[ObjectId], git_dir: &Path, mode: AccessMode) -> 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_bytes(hash.as_slice())?;
match mode {
AccessMode::ObjectData => {
let obj = odb.read(hash)?;
bytes.fetch_add(obj.len() as u64, std::sync::atomic::Ordering::Relaxed);
}
AccessMode::ObjectExists => {
assert!(odb.exists(hash));
}
}
Ok(())
},
)?;
Ok(bytes.load(std::sync::atomic::Ordering::Acquire))
}
fn do_gitoxide<C>(
hashes: &[ObjectId],
repo: &ThreadSafeRepository,
new_cache: impl Fn() -> C + Send + Sync + 'static,
) -> anyhow::Result<u64>
where
C: odb::pack::cache::DecodeEntry + Send + 'static,
{
use git_repository::prelude::FindExt;
let mut buf = Vec::new();
let mut bytes = 0u64;
let handle = repo.objects.to_cache().with_pack_cache(move || Box::new(new_cache()));
for hash in hashes {
let obj = handle.find(hash, &mut buf)?;
bytes += obj.data.len() as u64;
}
Ok(bytes)
}
enum AccessMode {
ObjectData,
ObjectExists,
}
fn do_gitoxide_in_parallel_sync<C>(
hashes: &[ObjectId],
repo: &ThreadSafeRepository,
new_cache: impl Fn() -> C + Send + Clone + Sync + 'static,
mode: AccessMode,
) -> anyhow::Result<u64>
where
C: odb::pack::cache::DecodeEntry + Send + 'static,
{
use git_repository::prelude::FindExt;
use rayon::prelude::*;
let bytes = std::sync::atomic::AtomicU64::default();
hashes.par_iter().try_for_each_init::<_, _, _, anyhow::Result<_>>(
move || {
(
Vec::new(),
repo.objects.to_cache().with_pack_cache({
let new_cache = new_cache.clone();
move || Box::new(new_cache())
}),
)
},
|(buf, objects), hash| {
match mode {
AccessMode::ObjectData => {
let obj = objects.find(hash, buf)?;
bytes.fetch_add(obj.data.len() as u64, std::sync::atomic::Ordering::Relaxed);
}
AccessMode::ObjectExists => {
use git_repository::prelude::Find;
assert!(objects.contains(hash), "each traversed object exists");
}
}
Ok(())
},
)?;
Ok(bytes.load(std::sync::atomic::Ordering::Acquire))
}
fn do_new_gitoxide_store_in_parallel<C>(
hashes: &[ObjectId],
objects_dir: &Path,
new_cache: impl Fn() -> C + Send + Sync + 'static,
mode: AccessMode,
store: Option<OwnShared<odb::Store>>,
) -> anyhow::Result<(std::sync::Arc<odb::Store>, u64)>
where
C: odb::pack::cache::DecodeEntry + Send + 'static,
{
use git_repository::prelude::FindExt;
let bytes = std::sync::atomic::AtomicU64::default();
let slots = std::env::args()
.nth(2)
.and_then(|num| num.parse().ok().map(odb::store::init::Slots::Given))
.unwrap_or_default();
let store = match store {
Some(store) => store,
None => OwnShared::new(odb::Store::at_opts(
objects_dir,
Vec::new(),
git_repository::odb::store::init::Options {
slots,
..Default::default()
},
)?),
};
let handle = Cache::from(store.to_handle()).with_pack_cache(move || Box::new(new_cache()));
git_repository::parallel::in_parallel(
hashes.chunks(1000),
None,
move |_| (Vec::new(), handle.clone()),
|hashes, (buf, cache)| {
for hash in hashes {
match mode {
AccessMode::ObjectData => {
let obj = cache.find(hash, buf)?;
bytes.fetch_add(obj.data.len() as u64, std::sync::atomic::Ordering::Relaxed);
}
AccessMode::ObjectExists => {
use git_repository::prelude::Find;
assert!(cache.contains(hash), "each traversed object exists");
}
}
}
Ok(())
},
git_repository::parallel::reduce::IdentityWithResult::<(), anyhow::Error>::default(),
)?;
Ok((store, bytes.load(std::sync::atomic::Ordering::Acquire)))
}
fn do_gitoxide_in_parallel_through_arc<C>(
hashes: &[ObjectId],
objects_dir: &Path,
new_cache: impl Fn() -> C + Send + Sync + Clone + 'static,
mode: AccessMode,
) -> anyhow::Result<u64>
where
C: odb::pack::cache::DecodeEntry + Send + 'static,
{
use git_repository::prelude::FindExt;
let bytes = std::sync::atomic::AtomicU64::default();
#[allow(deprecated)]
let odb = Arc::new(odb::linked::Store::at(objects_dir)?);
git_repository::parallel::in_parallel(
hashes.chunks(1000),
None,
move |_| {
(
Vec::new(),
odb.to_cache_arc().with_pack_cache({
let new_cache = new_cache.clone();
move || Box::new(new_cache())
}),
)
},
|hashes, (buf, odb)| {
for hash in hashes {
match mode {
AccessMode::ObjectData => {
let obj = odb.find(hash, buf)?;
bytes.fetch_add(obj.data.len() as u64, std::sync::atomic::Ordering::Relaxed);
}
AccessMode::ObjectExists => {
use git_repository::prelude::Find;
assert!(odb.contains(hash), "each traversed object exists");
}
}
}
Ok(())
},
git_repository::parallel::reduce::IdentityWithResult::<(), anyhow::Error>::default(),
)?;
Ok(bytes.load(std::sync::atomic::Ordering::Acquire))
}