Skip to content

Commit b8d1f22

Browse files
mivertowskiclaude
andcommitted
feat: expose per-actor metrics through runtime API
Gap analysis item 4.6: Add messages_sent, messages_received, input/output_queue_depth, state, gpu_launched fields to KernelMetrics. - Add kernel_metrics() to RingKernelRuntime trait (default impl via get_kernel) - Populate metrics from control block in all 4 backends: CPU, CUDA, WebGPU, Metal - CUDA reads gpu_launched flag and message counters - All backends populate queue depths and lifecycle state Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e66da41 commit b8d1f22

6 files changed

Lines changed: 79 additions & 3 deletions

File tree

crates/ringkernel-core/src/runtime.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,16 @@ pub trait RingKernelRuntime: Send + Sync {
387387
/// Get runtime metrics.
388388
fn metrics(&self) -> RuntimeMetrics;
389389

390+
/// Get per-kernel metrics for a specific kernel.
391+
///
392+
/// Returns detailed metrics for the kernel identified by `kernel_id`,
393+
/// including message counts, queue depths, uptime, state, and whether
394+
/// the kernel is GPU-launched. Returns `None` if the kernel is not found.
395+
fn kernel_metrics(&self, kernel_id: &KernelId) -> Option<KernelMetrics> {
396+
// Default: look up the kernel handle and delegate to its metrics()
397+
self.get_kernel(kernel_id).map(|handle| handle.metrics())
398+
}
399+
390400
/// Shutdown the runtime and terminate all kernels.
391401
async fn shutdown(&self) -> Result<()>;
392402
}

crates/ringkernel-core/src/telemetry.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,24 @@ pub struct KernelMetrics {
134134

135135
/// Host memory usage in bytes.
136136
pub host_memory_used: u64,
137+
138+
/// Total messages sent to this kernel.
139+
pub messages_sent: u64,
140+
141+
/// Total messages received from this kernel.
142+
pub messages_received: u64,
143+
144+
/// Current number of messages in the input queue.
145+
pub input_queue_depth: usize,
146+
147+
/// Current number of messages in the output queue.
148+
pub output_queue_depth: usize,
149+
150+
/// Current kernel state.
151+
pub state: crate::runtime::KernelState,
152+
153+
/// Whether the kernel has been launched on the GPU.
154+
pub gpu_launched: bool,
137155
}
138156

139157
impl Default for KernelMetrics {
@@ -148,6 +166,12 @@ impl Default for KernelMetrics {
148166
bytes_from_device: 0,
149167
gpu_memory_used: 0,
150168
host_memory_used: 0,
169+
messages_sent: 0,
170+
messages_received: 0,
171+
input_queue_depth: 0,
172+
output_queue_depth: 0,
173+
state: crate::runtime::KernelState::Created,
174+
gpu_launched: false,
151175
}
152176
}
153177
}

crates/ringkernel-cpu/src/kernel.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,9 @@ impl KernelHandleInner for CpuKernel {
328328

329329
fn metrics(&self) -> KernelMetrics {
330330
let telemetry = *self.telemetry.read();
331+
let state = *self.state.read();
332+
let messages_sent = self.message_counter.load(Ordering::Relaxed);
333+
let control = self.control.read();
331334
KernelMetrics {
332335
telemetry,
333336
kernel_id: self.id.to_string(),
@@ -338,6 +341,12 @@ impl KernelHandleInner for CpuKernel {
338341
bytes_from_device: 0,
339342
gpu_memory_used: 0,
340343
host_memory_used: 0,
344+
messages_sent,
345+
messages_received: control.messages_processed,
346+
input_queue_depth: self.input_queue.len(),
347+
output_queue_depth: self.output_queue.len(),
348+
state,
349+
gpu_launched: false, // CPU backend is never GPU-launched
341350
}
342351
}
343352

crates/ringkernel-cuda/src/kernel.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,18 @@ impl KernelHandleInner for CudaKernel {
374374
}
375375

376376
fn metrics(&self) -> KernelMetrics {
377-
self.metrics.read().clone()
377+
let mut metrics = self.metrics.read().clone();
378+
let state = *self.state.read();
379+
let cb = self.control_block.read().read().unwrap_or_default();
380+
metrics.kernel_id = self.id.to_string();
381+
metrics.uptime = self.created_at.elapsed();
382+
metrics.messages_sent = self.message_counter.load(Ordering::Relaxed);
383+
metrics.messages_received = cb.messages_processed;
384+
metrics.input_queue_depth = cb.input_queue_size() as usize;
385+
metrics.output_queue_depth = cb.output_queue_size() as usize;
386+
metrics.state = state;
387+
metrics.gpu_launched = self.gpu_launched.load(Ordering::Relaxed);
388+
metrics
378389
}
379390

380391
async fn activate(&self) -> Result<()> {

crates/ringkernel-metal/src/kernel.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,18 @@ impl KernelHandleInner for MetalKernel {
763763
}
764764

765765
fn metrics(&self) -> KernelMetrics {
766-
self.metrics.read().clone()
766+
let mut metrics = self.metrics.read().clone();
767+
let state = *self.state.read();
768+
let cb = self.read_control_block();
769+
metrics.kernel_id = self.id.to_string();
770+
metrics.uptime = self.created_at.elapsed();
771+
metrics.messages_sent = self.message_counter.load(Ordering::Relaxed);
772+
metrics.messages_received = cb.messages_processed();
773+
metrics.input_queue_depth = cb.input_queue_size() as usize;
774+
metrics.output_queue_depth = cb.output_queue_size() as usize;
775+
metrics.state = state;
776+
metrics.gpu_launched = false; // Metal is event-driven
777+
metrics
767778
}
768779

769780
async fn activate(&self) -> Result<()> {

crates/ringkernel-wgpu/src/kernel.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,18 @@ impl KernelHandleInner for WgpuKernel {
187187
}
188188

189189
fn metrics(&self) -> KernelMetrics {
190-
self.metrics.read().clone()
190+
let mut metrics = self.metrics.read().clone();
191+
let state = *self.state.read();
192+
let cb = self.control_block.read().read().unwrap_or_default();
193+
metrics.kernel_id = self.id.to_string();
194+
metrics.uptime = self.created_at.elapsed();
195+
metrics.messages_sent = self.message_counter.load(Ordering::Relaxed);
196+
metrics.messages_received = cb.messages_processed;
197+
metrics.input_queue_depth = cb.input_queue_size() as usize;
198+
metrics.output_queue_depth = cb.output_queue_size() as usize;
199+
metrics.state = state;
200+
metrics.gpu_launched = false; // WebGPU is event-driven, not persistently launched
201+
metrics
191202
}
192203

193204
async fn activate(&self) -> Result<()> {

0 commit comments

Comments
 (0)