From 45b0cd94ac728894b146777728cce7e87cd75e18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Heres?= Date: Mon, 6 Apr 2026 20:02:29 +0200 Subject: [PATCH] Skip type erasure in trace_future/trace_block when no tracer is set When no custom JoinSetTracer is registered (the common case), the trace_future and trace_block functions were still performing unnecessary type erasure (boxing as Box) and downcast operations on every spawned task. Profiling ClickBench queries showed trace_future's type erasure overhead accounted for ~12% of total CPU time across all queries. This adds a fast path that checks GLOBAL_TRACER.get().is_none() and returns the future/closure directly without the erase/trace/downcast pipeline when no custom tracer is configured. Co-Authored-By: Claude Opus 4.6 (1M context) --- datafusion/common-runtime/src/trace_utils.rs | 22 +++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/datafusion/common-runtime/src/trace_utils.rs b/datafusion/common-runtime/src/trace_utils.rs index f8adbe8825bc1..dde266b220a4a 100644 --- a/datafusion/common-runtime/src/trace_utils.rs +++ b/datafusion/common-runtime/src/trace_utils.rs @@ -132,21 +132,24 @@ where F: Future + Send + 'static, T: Send + 'static, { - // Erase the future’s output type first: + // Fast path: if no custom tracer is set, avoid type erasure overhead + if GLOBAL_TRACER.get().is_none() { + return future.boxed(); + } + + // Slow path: erase the future's output type, pass through tracer, then downcast let erased_future = async move { let result = future.await; Box::new(result) as Box } .boxed(); - // Forward through the global tracer: get_tracer() .trace_future(erased_future) - // Downcast from `Box` back to `T`: .map(|any_box| { *any_box .downcast::() - .expect("Tracer must preserve the future’s output type!") + .expect("Tracer must preserve the future's output type!") }) .boxed() } @@ -169,20 +172,23 @@ where F: FnOnce() -> T + Send + 'static, T: Send + 'static, { - // Erase the closure’s return type first: + // Fast path: if no custom tracer is set, avoid type erasure overhead + if GLOBAL_TRACER.get().is_none() { + return Box::new(f); + } + + // Slow path: erase, trace, downcast let erased_closure = Box::new(|| { let result = f(); Box::new(result) as Box }); - // Forward through the global tracer: let traced_closure = get_tracer().trace_block(erased_closure); - // Downcast from `Box` back to `T`: Box::new(move || { let any_box = traced_closure(); *any_box .downcast::() - .expect("Tracer must preserve the closure’s return type!") + .expect("Tracer must preserve the closure's return type!") }) }