feat: group compilation statistics (#2231)
Some checks failed
tinymist::auto_tag / auto-tag (push) Has been cancelled
tinymist::ci / Duplicate Actions Detection (push) Has been cancelled
tinymist::gh_pages / build-gh-pages (push) Has been cancelled
tinymist::ci / Check Clippy, Formatting, Completion, Documentation, and Tests (Linux) (push) Has been cancelled
tinymist::ci / Check Minimum Rust version and Tests (Windows) (push) Has been cancelled
tinymist::ci / prepare-build (push) Has been cancelled
tinymist::ci / announce (push) Has been cancelled
tinymist::ci / build (push) Has been cancelled

This commit is contained in:
Myriad-Dreamin 2025-11-09 08:16:02 +08:00 committed by GitHub
parent 9f8501c42a
commit c63162959d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 37 additions and 28 deletions

View file

@ -104,8 +104,21 @@ pub struct QueryStatBucket {
pub data: Arc<Mutex<QueryStatBucketData>>,
}
impl QueryStatBucket {
/// Increment the query statistic.
pub fn increment(&self, elapsed: Duration) {
let mut data = self.data.lock();
data.query += 1;
data.total += elapsed;
data.min = data.min.min(elapsed);
data.max = data.max.max(elapsed);
}
}
/// A guard for the query statistic.
pub struct QueryStatGuard {
/// The bucket of the query statistic for any file.
pub bucket_any: Option<QueryStatBucket>,
/// The bucket of the query statistic.
pub bucket: QueryStatBucket,
/// The start time of the query.
@ -115,11 +128,10 @@ pub struct QueryStatGuard {
impl Drop for QueryStatGuard {
fn drop(&mut self) {
let elapsed = self.since.elapsed();
let mut data = self.bucket.data.lock();
data.query += 1;
data.total += elapsed;
data.min = data.min.min(elapsed);
data.max = data.max.max(elapsed);
self.bucket.increment(elapsed);
if let Some(bucket) = self.bucket_any.as_ref() {
bucket.increment(elapsed);
}
}
}
@ -135,17 +147,17 @@ impl QueryStatGuard {
#[derive(Default)]
pub struct AnalysisStats {
/// The query statistics.
pub query_stats: FxDashMap<FileId, FxDashMap<&'static str, QueryStatBucket>>,
pub query_stats: Arc<FxDashMap<Option<FileId>, FxDashMap<&'static str, QueryStatBucket>>>,
}
impl AnalysisStats {
/// Gets a statistic guard for a query.
pub fn stat(&self, id: FileId, query: &'static str) -> QueryStatGuard {
pub fn stat(&self, id: Option<FileId>, name: &'static str) -> QueryStatGuard {
let stats = &self.query_stats;
let entry = stats.entry(id).or_default();
let entry = entry.entry(query).or_default();
let get = |v| stats.entry(v).or_default().entry(name).or_default().clone();
QueryStatGuard {
bucket: entry.clone(),
bucket_any: if id.is_some() { Some(get(None)) } else { None },
bucket: get(id),
since: tinymist_std::time::Instant::now(),
}
}
@ -160,7 +172,11 @@ impl AnalysisStats {
for refs2 in queries.iter() {
let query = refs2.key();
let bucket = refs2.value().data.lock().clone();
let name = format!("{id:?}:{query}").replace('\\', "/");
let name = match id {
Some(id) => format!("{id:?}:{query}"),
None => query.to_string(),
};
let name = name.replace('\\', "/");
data.push((name, bucket));
}
}

View file

@ -3,7 +3,6 @@
use comemo::Track;
use ecow::*;
use tinymist_std::typst::{TypstDocument, TypstPagedDocument};
use tinymist_world::DETACHED_ENTRY;
use typst::World;
use typst::engine::{Engine, Route, Sink, Traced};
use typst::foundations::{Context, Label, Scopes, Styles, Value};
@ -48,8 +47,7 @@ pub fn analyze_expr_(world: &dyn World, node: &SyntaxNode) -> EcoVec<(Value, Opt
return analyze_expr_(world, child);
}
let id = node.span().id().unwrap_or_else(|| *DETACHED_ENTRY);
let _guard = GLOBAL_STATS.stat(id, "analyze_expr");
let _guard = GLOBAL_STATS.stat(node.span().id(), "analyze_expr");
return typst::trace::<TypstPagedDocument>(world, node.span());
}
};
@ -68,8 +66,7 @@ pub fn analyze_import_(world: &dyn World, source: &SyntaxNode) -> (Option<Value>
return (Some(source.clone()), Some(source));
}
let id = source_span.id().unwrap_or_else(|| *DETACHED_ENTRY);
let _guard = GLOBAL_STATS.stat(id, "analyze_import");
let _guard = GLOBAL_STATS.stat(source_span.id(), "analyze_import");
let introspector = Introspector::default();
let traced = Traced::default();
@ -124,7 +121,7 @@ pub struct DynLabel {
pub fn analyze_labels(document: &TypstDocument) -> (Vec<DynLabel>, usize) {
let mut output = vec![];
let _guard = GLOBAL_STATS.stat(*DETACHED_ENTRY, "analyze_labels");
let _guard = GLOBAL_STATS.stat(None, "analyze_labels");
// Labels in the document.
for elem in document.introspector().all() {

View file

@ -1115,7 +1115,7 @@ impl SharedContext {
}
fn query_stat(&self, id: TypstFileId, query: &'static str) -> QueryStatGuard {
self.analysis.stats.stat(id, query)
self.analysis.stats.stat(Some(id), query)
}
/// Check on a module before really needing them. But we likely use them

View file

@ -21,7 +21,7 @@ pub(crate) fn do_compute_docstring(
docs: String,
kind: DefKind,
) -> Option<DocString> {
let _guard = GLOBAL_STATS.stat(fid, "compute_docstring");
let _guard = GLOBAL_STATS.stat(Some(fid), "compute_docstring");
let checker = DocsChecker {
fid,

View file

@ -662,11 +662,7 @@ impl CompileHandler<LspCompilerFeat, ProjectInsStateExt> for CompileHandlerImpl
let Some(compile_fn) = s.may_compile(&c.handler) else {
continue;
};
let id = s
.snapshot()
.world()
.main_id()
.unwrap_or_else(|| *DETACHED_ENTRY);
let id = s.snapshot().world().main_id();
s.ext.compiling_since = Some(tinymist_std::time::now());
spawn_cpu(move || {

View file

@ -10,7 +10,7 @@ use typst::foundations::Bytes;
use typst::{syntax::VirtualPath, World};
use super::prelude::*;
use crate::project::LspComputeGraph;
use crate::project::{EntryReader, LspComputeGraph};
use crate::world::{base::ShadowApi, EntryState, TaskInputs};
#[derive(Debug, Serialize, Deserialize)]
@ -232,7 +232,7 @@ fn render_symbols(
..TaskInputs::default()
});
let _guard = GLOBAL_STATS.stat(forked.main(), "render_symbols");
let _guard = GLOBAL_STATS.stat(forked.main_id(), "render_symbols");
forked
.map_shadow_by_id(forked.main(), Bytes::from_string(math_shaping_text))
.map_err(|e| error_once!("cannot map shadow", err: e))

View file

@ -6,7 +6,7 @@ use std::sync::{Arc, OnceLock};
use std::{ops::DerefMut, pin::Pin};
use reflexo::ImmutPath;
use reflexo_typst::{Bytes, CompilationTask, ExportComputation, DETACHED_ENTRY};
use reflexo_typst::{Bytes, CompilationTask, ExportComputation};
use sync_ls::{internal_error, just_future};
use tinymist_project::LspWorld;
use tinymist_query::{OnExportRequest, OnExportResponse, PagedExportResponse, GLOBAL_STATS};
@ -74,7 +74,7 @@ impl ServerState {
..TaskInputs::default()
});
let id = snap.world().main_id().unwrap_or_else(|| *DETACHED_ENTRY);
let id = snap.world().main_id();
let _guard = GLOBAL_STATS.stat(id, "export");
let is_html = matches!(task, ProjectTask::ExportHtml { .. });