fixed device timers for cuda and refactored the ComputeKernels - #1
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
CUDA synchronization, compatibility delegation, and one XPU queue allocation remain incorrect.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Introduces unified CPU, CUDA, and XPU kernel timing while consolidating duplicated backend paths.
Changes:
- Adds backend-specific timer abstractions.
- Refactors seven compute kernels around
timed_regions. - Adds device synchronization and queue-aware allocation support.
File summaries
| File | Description |
|---|---|
src/SimAIBench/kernel.py |
Implements unified timing and refactors compute kernels. |
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 4
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| for q in default_queues: | ||
| q.wait() | ||
| elif device == "cuda" and CUPY_AVAILABLE: | ||
| cp.cuda.Stream.null.synchronize() |
There was a problem hiding this comment.
Use this instead:
cp.cuda.Device().synchronize()
| with t: | ||
| y = xp.empty(n, dtype=xp.float32, **t.alloc_kwargs) | ||
| x = xp.empty(n, dtype=xp.float32, **t.alloc_kwargs) | ||
| idx = xp.random.randint(0, n, size=n, dtype=xp.int32) |
| def sync(self,device:str): | ||
| """Deprecated: use module-level device_sync(). | ||
| """ | ||
| if device=="xpu": | ||
| for q in default_queues: | ||
| q.wait() | ||
| else: | ||
| pass |
There was a problem hiding this comment.
🔵 Needs a closer look
CUDA synchronization and the deprecated synchronization API do not yet satisfy their documented behavior.
Review details
Suppressed comments (2)
src/SimAIBench/kernel.py:542
- Synchronizing only
Stream.nulldoes not drain pending work on another current CUDA stream. Because_ticis started immediately afterward while the events are recorded on the current stream,host_dtcan include that earlier work, violating the stated per-kernel timing semantics. Synchronize the device before starting the host clock.
cp.cuda.Stream.null.synchronize()
src/SimAIBench/kernel.py:619
- The deprecation text promises the new module-level behavior, but this method still retains its old XPU-only implementation, so
ComputeKernel.sync("cuda")remains a no-op instead of preserving compatibility by delegating. Forward directly todevice_sync.
"""Deprecated: use module-level device_sync().
"""
- Files reviewed: 1/1 changed files
- Comments generated: 0 new
- Review effort level: Balanced
There was a problem hiding this comment.
🔵 Needs a closer look
ScatterAdd still creates its XPU index array outside the timed queue.
Review details
Suppressed comments (1)
src/SimAIBench/kernel.py:724
- On XPU,
idxis still allocated on dpnp's default queue rather than the queue measured bySyclQueueTimer. Consequentlyq.wait()does not cover this operation, so the timing can omit it and the method may return with random-number work outstanding. Create the index array ont's queue as well.
idx = xp.random.randint(0, n, size=n, dtype=xp.int32)
- Files reviewed: 1/1 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Background: ComputeKernel subclasses return
(host_dt, device_dt), and the workflow relies on both fields:run_countmode accumulatesdevice_dt,run_timemode accumulateshost_dt(simulation.py). On thecudaandcpupaths, both fields were produced bytime.time()around asynchronous launches with no synchronization, so both measured kernel submission while the actual GPU work ran untimed.Consequences:
device_dton CUDA was meaningless. Thexpupath was always correct viadpctl.SyclTimer; this PR brings the other backends to the same semantics.Changes
Unified timing interface:
timed_regions(device)yields per-region timers with common implementation on all backends:host_dt= wall time including device execution,device_dt= device-only execution time,host_dt ≥ device_dt.CudaEventTimer: CUDA event pair + sync at exit (entry sync so previously pending async work isn't billed to the kernel).SyclQueueTimer: wrapsdpctl.SyclTimer; theq.wait()now lives in the timer, not in each kernel body.HostTimer:perf_counter; wall = execution on synchronous NumPy.Kernels collapse to a single code path – the duplicated if device == "xpu" branches are removed from all seven kernels; xpu's run-per-queue and average behavior is preserved via the region generator.
Fixes a queue mismatch in
GenerateRandomNumber(xpu) — the RNG launched on the default queue while the timer watched queue q; allocations now carry the timed queue.time.time() → time.perf_counter()(monotonic).ComputeKernel.sync(previously dead code, no call sites) delegates to a new module-level device_sync(device); kept for API compatibility.ScatterAddleft as shipped (kernel body still lacks implementation), wrapped for contract consistency and flagged.Validation: all kernels smoke-tested on cpu (
host_dt == device_dt > 0, registries intact); on GH200,host_dt ≥ device_dtholds with the difference at launch-overhead scale, and calibratedrun_timebudgets now correspond to actual device occupancy.