-
Notifications
You must be signed in to change notification settings - Fork 27.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Store aggregate read/execute count statistics (vercel/turborepo#8286)
### Why? I want to determine percent "cache hit" rates for tasks. Tasks with very low task hit rates should likely have their annotations removed. Eventually, we might be able to use this information in a more automated way, by leaving the annotation in, but skipping the caching for low-cache-hit tasks. ### What? This implementation only logs persistent tasks, which should compromise all or the majority of tasks we care about for memory usage. The implementation should bail out quickly if caching is disabled, so it should be okay to leave in release builds, which is important for making it easy to gather statistics from willing users. ### Testing Run included unit tests! This is used as part of canary...bgw/cache-hit-stats
- Loading branch information
Showing
6 changed files
with
474 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
use std::{ | ||
hash::BuildHasherDefault, | ||
sync::{Arc, OnceLock}, | ||
}; | ||
|
||
use dashmap::DashMap; | ||
use rustc_hash::FxHasher; | ||
use serde::{ser::SerializeMap, Serialize, Serializer}; | ||
use turbo_tasks::{registry, FunctionId}; | ||
|
||
/// An API for optionally enabling, updating, and reading aggregated statistics. | ||
#[derive(Default)] | ||
pub struct TaskStatisticsApi { | ||
inner: OnceLock<Arc<TaskStatistics>>, | ||
} | ||
|
||
impl TaskStatisticsApi { | ||
pub fn enable(&self) -> &Arc<TaskStatistics> { | ||
self.inner.get_or_init(|| { | ||
Arc::new(TaskStatistics { | ||
inner: DashMap::with_hasher(Default::default()), | ||
}) | ||
}) | ||
} | ||
|
||
pub fn is_enabled(&self) -> bool { | ||
self.inner.get().is_some() | ||
} | ||
|
||
// Calls `func` if statistics have been enabled (via | ||
// [`TaskStatisticsApi::enable`]). | ||
pub fn map<T>(&self, func: impl FnOnce(&Arc<TaskStatistics>) -> T) -> Option<T> { | ||
self.get().map(func) | ||
} | ||
|
||
// Calls `func` if statistics have been enabled (via | ||
// [`TaskStatisticsApi::enable`]). | ||
pub fn get(&self) -> Option<&Arc<TaskStatistics>> { | ||
self.inner.get() | ||
} | ||
} | ||
|
||
/// A type representing the enabled state of [`TaskStatisticsApi`]. Implements | ||
/// [`serde::Serialize`]. | ||
pub struct TaskStatistics { | ||
inner: DashMap<FunctionId, TaskFunctionStatistics, BuildHasherDefault<FxHasher>>, | ||
} | ||
|
||
impl TaskStatistics { | ||
pub(crate) fn increment_cache_hit(&self, function_id: FunctionId) { | ||
self.with_task_type_statistics(function_id, |stats| stats.cache_hit += 1) | ||
} | ||
|
||
pub(crate) fn increment_cache_miss(&self, function_id: FunctionId) { | ||
self.with_task_type_statistics(function_id, |stats| stats.cache_miss += 1) | ||
} | ||
|
||
fn with_task_type_statistics( | ||
&self, | ||
task_function_id: FunctionId, | ||
func: impl Fn(&mut TaskFunctionStatistics), | ||
) { | ||
func(self.inner.entry(task_function_id).or_default().value_mut()) | ||
} | ||
} | ||
|
||
/// Statistics for an individual function. | ||
#[derive(Default, Serialize)] | ||
struct TaskFunctionStatistics { | ||
cache_hit: u32, | ||
cache_miss: u32, | ||
} | ||
|
||
impl Serialize for TaskStatistics { | ||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
where | ||
S: Serializer, | ||
{ | ||
let mut map = serializer.serialize_map(Some(self.inner.len()))?; | ||
for entry in &self.inner { | ||
let key = registry::get_function_global_name(*entry.key()); | ||
map.serialize_entry(key, entry.value())?; | ||
} | ||
map.end() | ||
} | ||
} |
Oops, something went wrong.