feat(tvix/eval): Put interner in a thread-local RefCell

Rather than making the interner be a global lazy_static mutex, put it in
a thread-local RefCell. This doesn't change anything in terms of
sharing (since we're currently actually just single threaded), but
avoids the overhead of a mutex, for a nice performance boost (compared
to the mutex version):

hello outpath           time:   [726.71 ms 729.79 ms 735.69 ms]
                        change: [-5.7277% -3.9733% -2.1144%] (p = 0.00 < 0.05)
                        Performance has improved.

Change-Id: I240b238dcbaf854ebafc3017b4425fb7d7b91b03
Reviewed-on: https://cl.tvl.fyi/c/depot/+/12048
Tested-by: BuildkiteCI
Reviewed-by: flokli <flokli@flokli.de>
Autosubmit: aspen <root@gws.fyi>
This commit is contained in:
Aspen Smith 2024-07-28 12:36:24 -04:00 committed by aspen
parent 6366cee717
commit 6559ab4cf5

View file

@ -4,18 +4,17 @@
//! level, allowing us to shave off some memory overhead and only
//! paying the cost when creating new strings.
use bstr::{BStr, BString, ByteSlice, Chars};
use lazy_static::lazy_static;
use rnix::ast;
use rustc_hash::{FxHashMap, FxHashSet};
use std::alloc::dealloc;
use std::alloc::{alloc, handle_alloc_error, Layout};
use std::borrow::{Borrow, Cow};
use std::cell::RefCell;
use std::ffi::c_void;
use std::fmt::{self, Debug, Display};
use std::hash::Hash;
use std::ops::Deref;
use std::ptr::{self, NonNull};
use std::sync::Mutex;
use std::{mem, slice};
use serde::de::{Deserializer, Visitor};
@ -423,21 +422,21 @@ impl InternerInner {
}
#[derive(Default)]
struct Interner(Mutex<InternerInner>);
struct Interner(RefCell<InternerInner>);
impl Interner {
pub fn intern(&self, s: &[u8]) -> NixString {
self.0.lock().unwrap().intern(s)
self.0.borrow_mut().intern(s)
}
#[cfg(feature = "no_leak")]
pub fn is_interned_string(&self, string: &NixString) -> bool {
self.0.lock().unwrap().interned_strings.contains(&string.0)
self.0.borrow().interned_strings.contains(&string.0)
}
}
lazy_static! {
static ref INTERNER: Interner = Interner::default();
thread_local! {
static INTERNER: Interner = Interner::default();
}
/// Nix string values
@ -472,7 +471,7 @@ impl Drop for NixString {
#[cfg(feature = "no_leak")]
fn drop(&mut self) {
if INTERNER.is_interned_string(self) {
if INTERNER.with(|i| i.is_interned_string(self)) {
return;
}
@ -724,7 +723,7 @@ impl NixString {
&& contents.len() <= INTERN_THRESHOLD
&& context.is_none()
{
return INTERNER.intern(contents);
return INTERNER.with(|i| i.intern(contents));
}
Self::new_inner(contents, context)