diff --git a/Cargo.lock b/Cargo.lock index bf9d6bd00..35bbb274d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -451,6 +451,7 @@ dependencies = [ "libc", "libz-sys", "log", + "lru", "make-cmd", "once_cell", "percent-encoding", @@ -587,6 +588,12 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "lru" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efa59af2ddfad1854ae27d75009d538d0998b4b2fd47083e743ac1a10e46c60" + [[package]] name = "lzma-sys" version = "0.1.20" diff --git a/Cargo.toml b/Cargo.toml index 7a90c9be6..91e20a9fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,6 +94,10 @@ version = "1" version = "0.4" features = ["std"] +[dependencies.lru] +version = "0.12" +default-features = false + [dependencies.regex] version = "1" default-features = false diff --git a/src/cinnabar/manifest.rs b/src/cinnabar/manifest.rs index 00c963f60..75e8b6b6f 100644 --- a/src/cinnabar/manifest.rs +++ b/src/cinnabar/manifest.rs @@ -2,9 +2,13 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use std::cell::RefCell; +use std::cmp; use std::io::{self, Write}; +use std::num::NonZeroUsize; use either::Either; +use lru::LruCache; use crate::git::{git_oid_type, CommitId, MalformedTree, TreeId, TreeIsh}; use crate::hg::{HgFileAttr, HgFileId, ManifestEntry}; @@ -31,11 +35,47 @@ impl TreeIsh for GitManifestId { } } +#[derive(Clone)] pub struct GitManifestTree(RawTree); +thread_local! { + static MANIFEST_TREE_CACHE: RefCell<(LruCache, usize, usize)> = RefCell::new((LruCache::unbounded(), 0, 0)); +} + impl GitManifestTree { pub fn read(oid: GitManifestTreeId) -> Option { - RawTree::read(oid.into()).map(Self) + MANIFEST_TREE_CACHE.with(|cache| { + let (lru_cache, queries, misses) = &mut *cache.borrow_mut(); + *queries += 1; + let result = lru_cache + .try_get_or_insert(oid, || { + *misses += 1; + RawTree::read(oid.into()).map(Self).ok_or(()) + }) + .ok() + .cloned(); + let queries_limit = cmp::max(100, lru_cache.len() / 2); + if *queries >= queries_limit { + let miss_rate = *misses / (*queries / 10); + if (lru_cache.cap() == NonZeroUsize::MAX) || miss_rate >= 7 { + lru_cache.resize( + NonZeroUsize::new(lru_cache.len() + lru_cache.len() / 2 + 1).unwrap(), + ); + } + debug!( + target: "manifesttreecache", + "cap: {}, len: {} ; {} misses in the last {} queries ({:.1}%)", + lru_cache.cap(), + lru_cache.len(), + *misses, + *queries, + (*misses as f64) * 100.0 / (*queries as f64) + ); + *queries = 0; + *misses = 0; + } + result + }) } pub fn read_treeish>(t: T) -> Option { diff --git a/src/libgit.rs b/src/libgit.rs index a6c803d31..2f58580b2 100644 --- a/src/libgit.rs +++ b/src/libgit.rs @@ -5,6 +5,7 @@ use std::ffi::{c_void, CStr, CString, OsStr, OsString}; use std::fmt; use std::io::{self, Write}; +use std::mem; use std::num::ParseIntError; use std::os::raw::{c_char, c_int, c_long, c_uint, c_ulong, c_ushort}; use std::str::FromStr; @@ -313,6 +314,17 @@ impl RawObject { } } +impl Clone for RawObject { + fn clone(&self) -> Self { + let mut cloned = strbuf::new(); + cloned.extend_from_slice(self.as_bytes()); + let buf = cloned.as_ptr() as *const _; + let len = cloned.as_bytes().len(); + mem::forget(cloned); + RawObject { buf, len } + } +} + impl Drop for RawObject { fn drop(&mut self) { unsafe { @@ -323,7 +335,7 @@ impl Drop for RawObject { macro_rules! raw_object { ($t:ident | $oid_type:ident => $name:ident) => { - #[derive(Deref)] + #[derive(Deref, Clone)] pub struct $name(RawObject); impl $name {