dynamo_runtime/compute/macros.rs
1// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Zero-overhead macros for compute task execution with optional validation
5//!
6//! These macros provide size-aware execution strategies:
7//! - `compute_small!`: Direct inline execution for tasks <100μs
8//! - `compute_medium!`: Semaphore-guarded block_in_place for tasks 100μs-1ms
9//! - `compute_large!`: Rayon offload for tasks >1ms
10//!
11//! When the `compute-validation` feature is enabled, these macros will
12//! time execution and emit warnings if tasks are misclassified.
13
14/// Execute a small compute task (<100μs) directly inline.
15///
16/// This macro has zero overhead and simply executes the expression directly.
17/// When validation is enabled, it will warn if the task takes >100μs.
18///
19/// # Example
20/// ```
21/// # use dynamo_runtime::compute_small;
22/// let result = compute_small!(2 + 2);
23/// assert_eq!(result, 4);
24/// ```
25#[macro_export]
26macro_rules! compute_small {
27 ($expr:expr) => {{
28 #[cfg(feature = "compute-validation")]
29 let _start = std::time::Instant::now();
30
31 let result = $expr; // Direct execution, zero overhead
32
33 #[cfg(feature = "compute-validation")]
34 $crate::compute::validation::validate_small(_start.elapsed());
35
36 result
37 }};
38}
39
40/// Execute a medium compute task (100μs-1ms) with intelligent scheduling.
41///
42/// This macro first tries to use thread-local context if available (on Tokio worker threads).
43/// If no thread-local context, it requires a pool parameter.
44///
45/// # Example
46/// ```ignore
47/// # use dynamo_runtime::{compute_medium, compute::ComputePool};
48/// # async fn example(pool: &ComputePool) {
49/// // With thread-local context (on worker thread)
50/// let result = compute_medium!({
51/// (0..1000).map(|i| i * 2).sum::<i32>()
52/// }).await;
53///
54/// // Or with explicit pool (fallback)
55/// let result = compute_medium!(pool, {
56/// (0..1000).map(|i| i * 2).sum::<i32>()
57/// }).await;
58/// # }
59/// ```
60#[macro_export]
61macro_rules! compute_medium {
62 // Thread-local version (no pool parameter)
63 ($expr:expr) => {{
64 #[cfg(feature = "compute-validation")]
65 let _start = std::time::Instant::now();
66
67 let result = async {
68 // Try thread-local context first
69 if let Ok(_permit) = $crate::compute::thread_local::try_acquire_block_permit() {
70 // Got permit - use block_in_place
71 Ok(tokio::task::block_in_place(|| {
72 let r = $expr;
73 drop(_permit); // Release ASAP
74 r
75 }))
76 } else if let Some(pool) = $crate::compute::thread_local::get_pool() {
77 // No permit but have pool - offload
78 pool.execute(|| $expr).await
79 } else {
80 // No context available - fall back to inline execution
81 // This may block the async runtime but ensures the macro always works
82 tracing::warn!("compute_medium: No thread-local context, executing inline (may block async runtime)");
83 Ok($expr)
84 }
85 }
86 .await?;
87
88 #[cfg(feature = "compute-validation")]
89 $crate::compute::validation::validate_medium(_start.elapsed());
90
91 result
92 }};
93
94 // Explicit pool version (fallback)
95 ($pool:expr, $expr:expr) => {{
96 #[cfg(feature = "compute-validation")]
97 let _start = std::time::Instant::now();
98
99 let result = async {
100 // Try thread-local permits first, fall back to pool
101 if let Ok(_permit) = $crate::compute::thread_local::try_acquire_block_permit() {
102 // Got permit - use block_in_place
103 Ok(tokio::task::block_in_place(|| {
104 let r = $expr;
105 drop(_permit); // Release ASAP
106 r
107 }))
108 } else {
109 // No permit available - offload to provided pool
110 $pool.execute(|| $expr).await
111 }
112 }
113 .await?;
114
115 #[cfg(feature = "compute-validation")]
116 $crate::compute::validation::validate_medium(_start.elapsed());
117
118 result
119 }};
120}
121
122/// Execute a large compute task (>1ms) on the Rayon thread pool.
123///
124/// This macro always offloads to Rayon as the overhead is negligible
125/// compared to the computation time.
126///
127/// # Example
128/// ```ignore
129/// # use dynamo_runtime::{compute_large, compute::ComputePool};
130/// # async fn example(pool: &ComputePool) {
131/// // With thread-local context
132/// let result = compute_large!({
133/// expensive_matrix_multiplication()
134/// }).await;
135///
136/// // Or with explicit pool
137/// let result = compute_large!(pool, {
138/// expensive_matrix_multiplication()
139/// }).await;
140/// # }
141/// ```
142#[macro_export]
143macro_rules! compute_large {
144 // Thread-local version
145 ($expr:expr) => {{
146 #[cfg(feature = "compute-validation")]
147 let _start = std::time::Instant::now();
148
149 let result = async {
150 if let Some(pool) = $crate::compute::thread_local::get_pool() {
151 pool.execute(|| $expr).await
152 } else {
153 // No pool available - fall back to inline execution
154 // Warning: Large tasks inline will severely block the async runtime
155 tracing::warn!("compute_large: No thread-local context, executing inline (will block async runtime!)");
156 Ok($expr)
157 }
158 }
159 .await?;
160
161 #[cfg(feature = "compute-validation")]
162 $crate::compute::validation::validate_large(_start.elapsed());
163
164 result
165 }};
166
167 // Explicit pool version
168 ($pool:expr, $expr:expr) => {{
169 #[cfg(feature = "compute-validation")]
170 let _start = std::time::Instant::now();
171
172 let result = $pool.execute(|| $expr).await?;
173
174 #[cfg(feature = "compute-validation")]
175 $crate::compute::validation::validate_large(_start.elapsed());
176
177 result
178 }};
179}