Skip to content

Add selective task-timing slots for lightweight dispatch/finish timing#1359

Open
doraemonmj wants to merge 1 commit into
hw-native-sys:mainfrom
doraemonmj:pr/task-timing-slots
Open

Add selective task-timing slots for lightweight dispatch/finish timing#1359
doraemonmj wants to merge 1 commit into
hw-native-sys:mainfrom
doraemonmj:pr/task-timing-slots

Conversation

@doraemonmj

@doraemonmj doraemonmj commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds 16 fixed, per-run task-timing slots on the existing lightweight
device-phase transport. Orchestration tags selected tasks with a slot id
(0..15); the Scheduler folds each tagged task's AICPU dispatch/finish
cycles into that slot. The host resets slots before each run, reads them
back once after stream sync, and emits each complete slot as a device-clock
simpler_run.runner_run.device_wall.task_slot_N [STRACE] span.

Works with L2 swimlane disabled and in SIMPLER_DFX=0 builds — it starts
no collector threads and writes no per-task AICore records. Untagged tasks
pay only a cache-hot sentinel check (!= TASK_TIMING_SLOT_NONE); they never
read the sys counter.

Closes #1325.

Design

  • Transport: a fixed TaskTimingRecord[16] tail appended after the
    AicpuPhaseRecord region in the same device buffer — same base pointer,
    same per-run H2D reset and post-sync D2H copy.
  • ABI: RT2 carries the id in the 4-byte pad after
    PTO2TaskDescriptor::kernel_id[3], so the descriptor does not grow;
    static_asserts pin its size and packed_buffer_base offset.
  • Aggregation: per slot, min(dispatch) / max(finish) across Scheduler
    threads and across a MIX task's subtasks / an SPMD task's blocks. Reusing a
    slot merges into one window; distinct slots recover finish(B) - dispatch(A).
  • Boundaries match the L2 swimlane: dispatch = earliest gated
    DATA_MAIN_BASE publication; finish = latest FIN observation (after
    rmb(), before fanin/deferred-completion). Incomplete/unset slots are
    skipped, so a short/failed run leaks no stale data.
  • a5 host_build_graph uses its legacy Runtime::Task path, so it gets a
    small set_task_timing_slot(runtime, task_id, slot) compatibility setter
    rather than the RT2 L0TaskArgs field.

Coverage

Supports a2a3/a5, onboard/sim, and both tensormap_and_ringbuffer and
host_build_graph, plus unit / sim-e2e / onboard tests and device-phase /
L2-timing docs.

Verification

Unit — a2a3 (tests/ut/cpp/a2a3/test_task_timing_slots.cpp, 9/9 pass):
arg->descriptor propagation, slot bounds/sentinel, fixed-buffer layout +
offsets, cross-thread min/max reduction, incomplete-slot skip, per-run reset.

Build: a2a3sim + a5sim + a2a3 (onboard) all build; a2a3 cpput subset plus
the 9 new cases pass 100%.

e2e — a2a3, sim + onboard (tests/st/task_timing/task_timing_slots/, L2
swimlane OFF): 7 instances on sim and 7 on onboard, all pass, across both
tensormap_and_ringbuffer and host_build_graph:

test checks
test_distinct_slots_emit_markers chain, dispatch(slot1) >= finish(slot0)
test_duplicate_slot_merges_window 3 tasks, same slot -> one merged window > single-task baseline
test_spmd_task_aggregates_across_threads block_num=8 -> cross-thread min/max -> one complete slot
test_mix_task_aggregates_across_subtasks MIX AIC+AIV0+AIV1 subtasks fold into one slot (tensormap)

Measured [STRACE] (a2a3, ns, device clock):

  • sim: task_slot_0 ts=14600 dur=428500, task_slot_1 ts=444240 dur=49560
    -> dispatch(0) < finish(0) < dispatch(1) (dependency respected),
    whole-chain finish(1) - dispatch(0) = 479200.
  • onboard (Ascend910 via task-submit, real H2D/D2H):
    task_slot_0 ts=270620 dur=8280, task_slot_1 ts=284260 dur=7979,
    whole-chain 21619 — confirms H2D reset -> AICPU fold -> D2H tail readback
    -> emit on real silicon.

e2e — a5 host_build_graph, onboard (passing):
tests/st/task_timing/task_timing_a5hbg/test_task_timing_a5hbg.py::test_a5hbg_task_timing_slots_emit_markers,
~17.5s — the legacy add_task path end to end: both task_slot_0/task_slot_1
markers with positive duration, dispatch(slot1) >= finish(slot0), golden
correct, swimlane off.

Static / lint — local pre-commit (all pass): clang-format, clang-tidy,
cpplint, ruff (check + format), pyright, plus header / English-only /
trailing-whitespace / large-file gates.

Integration: rebases cleanly onto current main. The only conflict was
the Arg struct gaining both the upstream dispatch predicate_ and this PR's
task_timing_slot_ member — resolved as a union; the PTO2TaskDescriptor
size / packed_buffer_base-offset static_asserts still hold.

Not run locally — relies on CI: a5 tensormap_and_ringbuffer (its a5sim
e2e needs g++-15; this box has g++-12).

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements selective task-timing slots (0–15) to measure specific task dispatch-to-finish windows without enabling the full L2 swimlane. This feature is supported across both A2A3 and A5 runtimes, including host_build_graph and tensormap_and_ringbuffer. The implementation reuses existing padding in the task descriptor to avoid size growth, hooks into scheduler dispatch and completion to record cycles, and updates host-side runners to read back and emit these timing slots as [STRACE] spans. Comprehensive system and unit tests are also added. Feedback on the changes highlights an ABI compatibility issue in OrchestrationRuntimeOps, where inserting the new set_task_timing_slot function pointer in the middle of the struct shifts subsequent fields; it is recommended to append new pointers to the end of the struct to maintain ABI stability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/a5/runtime/host_build_graph/orchestration/orchestration_api.h Outdated
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Selective task-timing slots add 16 opt-in dispatch-to-finish windows for tagged tasks. The implementation carries slot metadata through orchestration and schedulers, aggregates records across threads, resolves host timestamps, emits STRACE spans, and adds examples, tests, and documentation.

Changes

Selective task-timing slots

Layer / File(s) Summary
Timing records and task metadata
src/common/platform/include/common/device_phase.h, src/common/platform/include/aicpu/device_phase_aicpu.h, src/*/runtime/.../pto_types.h, src/*/runtime/.../pto_runtime2_types.h
Defines 16 timing slots, fixed record tails, min/max reduction, timestamp resolution, validated task APIs, and ABI-preserving descriptor fields.
Task propagation and scheduler recording
src/a2a3/..., src/a5/runtime/..., src/*/runtime/.../scheduler/*
Propagates tags through RT2 and legacy orchestration paths, records dispatch before publication, and records FIN observations for tagged tasks.
Buffer readback and marker emission
src/a2a3/platform/sim/host/*, src/a5/platform/sim/host/*, src/common/platform/onboard/host/*, src/common/platform/{onboard,sim}/host/c_api_shared.cpp
Extends device-wall buffers, resets and resolves timing tails, exposes slot timestamps, and emits valid task_slot_N STRACE spans.
Examples, tests, and documentation
examples/workers/l2/task_timing_slots/*, tests/st/*, tests/ut/cpp/*, docs/dfx/*
Adds chain, duplicate-slot, mixed-task, SPMD, and legacy host-build-graph coverage alongside usage and semantic documentation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Orchestrator
  participant Scheduler
  participant DevicePhaseBuffer
  participant HostRunner
  participant STRACE
  Orchestrator->>Scheduler: submit tagged task
  Scheduler->>DevicePhaseBuffer: fold dispatch and finish cycles
  HostRunner->>DevicePhaseBuffer: reset before run and read after synchronization
  HostRunner->>STRACE: emit task_slot_N device-clock span
Loading

Poem

I’m a rabbit with sixteen clocks,
Tagging hops on scheduler rocks.
Dispatch goes first, finish follows,
Tiny slots hold timing swallows.
STRACE blooms where windows gleam—
A carrot-powered profiling dream.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.58% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes satisfy #1325 by adding 16 slots, propagation, reduction, reset/readback, marker emission, and test coverage across the targeted runtimes.
Out of Scope Changes check ✅ Passed The PR contents appear scoped to the task-timing-slot feature, its docs, and test coverage with no obvious unrelated changes.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding selective task-timing slots for lightweight timing capture.
Description check ✅ Passed The description is clearly related and matches the PR’s implementation, coverage, and design goals.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/a5/runtime/host_build_graph/aicpu/aicpu_executor.cpp (1)

770-774: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Complete the older task before the newer task for chronological accuracy.

Reorder the calls to ensure the implicitly completed older task (prev_running_id) is recorded before the newly finished task (completed_task_id). Since fold_task_finish reads the current system cycle for the timestamp, calling it in reverse chronological order can result in the task that finished first receiving a slightly later timestamp.

♻️ Proposed refactor
                 // Both the pending task (FIN observed here) and the running task
                 // (implicitly done — AICore overwrote COND before its FIN) complete.
-                fold_task_finish(runtime, completed_task_id, thread_idx);
                 fold_task_finish(runtime, prev_running_id, thread_idx);
+                fold_task_finish(runtime, completed_task_id, thread_idx);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/a5/runtime/host_build_graph/aicpu/aicpu_executor.cpp` around lines 770 -
774, In the completion handling block, reorder the fold_task_finish calls so the
older implicitly completed task prev_running_id is recorded before the newer
completed_task_id task. Keep both calls and their existing arguments unchanged.
src/common/platform/onboard/host/c_api_shared.cpp (1)

537-546: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard kTaskSlotNames size against NUM_TASK_TIMING_SLOTS drift, like the neighboring kPhases table does.

Both emit_device_phase_markers implementations declare kTaskSlotNames[NUM_TASK_TIMING_SLOTS] with exactly 16 literal entries but no compile-time check tying the two together, unlike the adjacent kPhases[] table which has static_assert(sizeof(kPhases)/sizeof(kPhases[0]) == NUM_AICPU_PHASES - 1, ...). If NUM_TASK_TIMING_SLOTS is ever changed without updating this literal list, the extra array slots silently zero-fill to nullptr, and the loop would pass a null name into STRACE_DEV_SPAN_AT for any newly-added slot index that reports a valid (dispatch < finish) interval.

  • src/common/platform/onboard/host/c_api_shared.cpp#L537-L546: add a static_assert(sizeof(kTaskSlotNames) / sizeof(kTaskSlotNames[0]) == NUM_TASK_TIMING_SLOTS, ...) next to the array.
  • src/common/platform/sim/host/c_api_shared.cpp#L494-L503: add the identical static_assert here as well.
🛡️ Proposed fix (apply to both files)
     static const char *const kTaskSlotNames[NUM_TASK_TIMING_SLOTS] = {
         "simpler_run.runner_run.device_wall.task_slot_0",  "simpler_run.runner_run.device_wall.task_slot_1",
         "simpler_run.runner_run.device_wall.task_slot_2",  "simpler_run.runner_run.device_wall.task_slot_3",
         "simpler_run.runner_run.device_wall.task_slot_4",  "simpler_run.runner_run.device_wall.task_slot_5",
         "simpler_run.runner_run.device_wall.task_slot_6",  "simpler_run.runner_run.device_wall.task_slot_7",
         "simpler_run.runner_run.device_wall.task_slot_8",  "simpler_run.runner_run.device_wall.task_slot_9",
         "simpler_run.runner_run.device_wall.task_slot_10", "simpler_run.runner_run.device_wall.task_slot_11",
         "simpler_run.runner_run.device_wall.task_slot_12", "simpler_run.runner_run.device_wall.task_slot_13",
         "simpler_run.runner_run.device_wall.task_slot_14", "simpler_run.runner_run.device_wall.task_slot_15",
     };
+    static_assert(
+        sizeof(kTaskSlotNames) / sizeof(kTaskSlotNames[0]) == NUM_TASK_TIMING_SLOTS,
+        "kTaskSlotNames[] must list every task-timing slot — add the new slot name here"
+    );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/common/platform/onboard/host/c_api_shared.cpp` around lines 537 - 546,
Add a compile-time size check immediately after kTaskSlotNames in both
src/common/platform/onboard/host/c_api_shared.cpp lines 537-546 and
src/common/platform/sim/host/c_api_shared.cpp lines 494-503, asserting its
element count equals NUM_TASK_TIMING_SLOTS; use the same static_assert pattern
as the neighboring kPhases table.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/workers/l2/task_timing_slots/test_task_timing_e2e.py`:
- Around line 187-211: Guard the baseline slot lookups in
test_duplicate_slot_merges_window by storing the results of
_slot_spans(base_err, 0) and _slot_spans(base_err, 1), asserting each list is
non-empty with a clear failure message, and only then indexing element zero to
compute base_single.

In `@src/a2a3/runtime/host_build_graph/runtime/pto_types.h`:
- Around line 235-249: Update set_task_timing_slot in the shown pto_types.h and
its sibling tensormap_and_ringbuffer/runtime/pto_types.h to preserve the sticky
first-error invariant: when the slot is out of range, record has_error and
error_msg only if no prior error exists, matching set_error(). Keep valid-slot
assignment and existing validation behavior unchanged.

In `@src/a2a3/runtime/tensormap_and_ringbuffer/runtime/pto_types.h`:
- Around line 266-280: Update set_task_timing_slot in both the shared
pto_types.h implementation and its host_build_graph fork to report out-of-range
slots through the existing sticky first-error mechanism, such as set_error(),
instead of assigning has_error and error_msg directly. Preserve the current
range validation and return behavior while ensuring an earlier diagnostic cannot
be overwritten.

---

Nitpick comments:
In `@src/a5/runtime/host_build_graph/aicpu/aicpu_executor.cpp`:
- Around line 770-774: In the completion handling block, reorder the
fold_task_finish calls so the older implicitly completed task prev_running_id is
recorded before the newer completed_task_id task. Keep both calls and their
existing arguments unchanged.

In `@src/common/platform/onboard/host/c_api_shared.cpp`:
- Around line 537-546: Add a compile-time size check immediately after
kTaskSlotNames in both src/common/platform/onboard/host/c_api_shared.cpp lines
537-546 and src/common/platform/sim/host/c_api_shared.cpp lines 494-503,
asserting its element count equals NUM_TASK_TIMING_SLOTS; use the same
static_assert pattern as the neighboring kPhases table.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c4d9be09-87c4-4c65-8f00-fdb155ca2c3c

📥 Commits

Reviewing files that changed from the base of the PR and between 2cf189b and 3c142f6.

📒 Files selected for processing (45)
  • docs/dfx/device-phases.md
  • docs/dfx/l2-swimlane-profiling.md
  • docs/dfx/l2-timing.md
  • examples/workers/l2/task_timing_slots/__init__.py
  • examples/workers/l2/task_timing_slots/kernels/orchestration/task_timing_orch.cpp
  • examples/workers/l2/task_timing_slots/test_task_timing_e2e.py
  • src/a2a3/platform/sim/host/device_runner.cpp
  • src/a2a3/runtime/host_build_graph/orchestration/pto_arg_with_deps.h
  • src/a2a3/runtime/host_build_graph/runtime/orchestrator_core/pto_orchestrator.cpp
  • src/a2a3/runtime/host_build_graph/runtime/pto_runtime2_types.h
  • src/a2a3/runtime/host_build_graph/runtime/pto_types.h
  • src/a2a3/runtime/host_build_graph/runtime/scheduler/scheduler_completion.cpp
  • src/a2a3/runtime/host_build_graph/runtime/scheduler/scheduler_context.h
  • src/a2a3/runtime/host_build_graph/runtime/scheduler/scheduler_dispatch.cpp
  • src/a2a3/runtime/tensormap_and_ringbuffer/orchestration/pto_arg_with_deps.h
  • src/a2a3/runtime/tensormap_and_ringbuffer/runtime/pto_orchestrator.cpp
  • src/a2a3/runtime/tensormap_and_ringbuffer/runtime/pto_runtime2_types.h
  • src/a2a3/runtime/tensormap_and_ringbuffer/runtime/pto_types.h
  • src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_completion.cpp
  • src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_context.h
  • src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_dispatch.cpp
  • src/a5/platform/sim/host/device_runner.cpp
  • src/a5/runtime/host_build_graph/aicpu/aicpu_executor.cpp
  • src/a5/runtime/host_build_graph/host/runtime_maker.cpp
  • src/a5/runtime/host_build_graph/orchestration/orchestration_api.h
  • src/a5/runtime/host_build_graph/runtime/runtime.cpp
  • src/a5/runtime/host_build_graph/runtime/runtime.h
  • src/a5/runtime/tensormap_and_ringbuffer/orchestration/pto_arg_with_deps.h
  • src/a5/runtime/tensormap_and_ringbuffer/runtime/pto_orchestrator.cpp
  • src/a5/runtime/tensormap_and_ringbuffer/runtime/pto_runtime2_types.h
  • src/a5/runtime/tensormap_and_ringbuffer/runtime/pto_types.h
  • src/a5/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_completion.cpp
  • src/a5/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_context.h
  • src/a5/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_dispatch.cpp
  • src/common/platform/include/aicpu/device_phase_aicpu.h
  • src/common/platform/include/common/device_phase.h
  • src/common/platform/onboard/host/c_api_shared.cpp
  • src/common/platform/onboard/host/device_runner_base.cpp
  • src/common/platform/onboard/host/device_runner_base.h
  • src/common/platform/sim/host/c_api_shared.cpp
  • src/common/platform/sim/host/device_runner_base.h
  • tests/st/a5/host_build_graph/task_timing/kernels/orchestration/task_timing_a5hbg_orch.cpp
  • tests/st/a5/host_build_graph/task_timing/test_task_timing_a5hbg.py
  • tests/ut/cpp/CMakeLists.txt
  • tests/ut/cpp/a2a3/test_task_timing_slots.cpp

Comment thread examples/workers/l2/task_timing_slots/test_task_timing_e2e.py Outdated
Comment thread src/a2a3/runtime/host_build_graph/runtime/pto_types.h
Comment thread src/a2a3/runtime/tensormap_and_ringbuffer/runtime/pto_types.h
@doraemonmj doraemonmj force-pushed the pr/task-timing-slots branch 4 times, most recently from 852a28b to dd4267a Compare July 15, 2026 11:04
Adds 16 fixed per-run task-timing slots on the existing device-phase
transport. Orchestration tags selected tasks with a slot id (0..15) and
the Scheduler folds each tagged task's AICPU dispatch/finish cycles into
that slot; the host resets slots per run, reads them back after stream
sync, and emits each complete slot as a device-clock task_slot_N [STRACE]
span. Works with L2 swimlane disabled and in SIMPLER_DFX=0 builds;
untagged tasks pay only a cache-hot sentinel check.

Covers a2a3/a5, onboard/sim, both tensormap_and_ringbuffer and
host_build_graph, including the legacy a5 host_build_graph add_task path.
Adds unit, sim e2e, and a5 onboard tests plus device-phase/L2-timing docs.

Relocated tests to tests/st/task_timing/ for better organization:
- task_timing_slots/ - RT2 e2e tests (tensormap_and_ringbuffer)
- task_timing_a5hbg/ - a5 host_build_graph legacy API tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Add lightweight selective task dispatch/finish timing slots

1 participant