From c509a0dd15c7fb860219ea52990fabe1e6f25f21 Mon Sep 17 00:00:00 2001 From: zhusy54 Date: Thu, 3 Sep 2026 17:17:24 +0800 Subject: [PATCH] Refactor: rename A5 HBG Resolver to Scheduler Use Scheduler consistently for the A5 HBG AICore worker role. Rename its state, helpers, constants, and tests without changing behavior. Name chip-swimlane task and schedule timing levels by role while preserving their numeric values and on-disk schema. --- docs/dfx/profiling-config-naming.md | 2 +- simpler_setup/tools/swimlane_converter.py | 14 +- .../host_build_graph/docs/profiling_levels.md | 12 +- .../scheduler/scheduler_completion.cpp | 10 +- .../runtime/scheduler/scheduler_context.h | 2 +- .../runtime/scheduler/scheduler_dispatch.cpp | 6 +- .../docs/profiling_levels.md | 12 +- .../scheduler/scheduler_completion.cpp | 10 +- .../runtime/scheduler/scheduler_context.h | 2 +- .../runtime/scheduler/scheduler_dispatch.cpp | 6 +- .../aicore/aicore_executor.cpp | 87 ++-- .../aicpu/aicore_lifecycle.cpp | 34 +- .../host_build_graph/aicpu/aicpu_executor.cpp | 2 +- .../host_build_graph/docs/profiling_levels.md | 19 +- .../host_build_graph/host/runtime_maker.cpp | 2 +- .../runtime/dispatch_payload.h | 2 +- .../scheduler/scheduler_completion.cpp | 10 +- .../runtime/scheduler/scheduler_completion.h | 42 +- .../runtime/scheduler/scheduler_context.h | 2 +- .../runtime/scheduler/scheduler_dispatch.cpp | 6 +- .../runtime/scheduler/scheduler_dispatch.h | 118 +++--- .../runtime/scheduler/scheduler_ready.h | 92 ++-- .../runtime/scheduler/scheduler_topology.h | 2 +- .../runtime/scheduler/scheduler_types.h | 46 +- .../docs/profiling_levels.md | 12 +- .../scheduler/scheduler_completion.cpp | 10 +- .../runtime/scheduler/scheduler_context.h | 2 +- .../runtime/scheduler/scheduler_dispatch.cpp | 4 +- .../host_build_graph/docs/GRAPH_EXECUTION.md | 2 +- .../aicpu/chip_swimlane_collector_aicpu.h | 4 +- .../include/common/chip_swimlane_profiling.h | 26 +- .../include/host/chip_swimlane_collector.h | 6 +- .../aicpu/chip_swimlane_collector_aicpu.cpp | 10 +- .../shared/host/chip_swimlane_collector.cpp | 4 +- .../empty_lifecycle/test_empty_lifecycle.py | 2 +- .../ut/cpp/a5/test_hbg_scheduler_dispatch.cpp | 398 +++++++++--------- tests/ut/cpp/a5/test_hbg_scheduler_ready.cpp | 17 +- 37 files changed, 521 insertions(+), 516 deletions(-) diff --git a/docs/dfx/profiling-config-naming.md b/docs/dfx/profiling-config-naming.md index 1b205671b1..c6b65a6c6c 100644 --- a/docs/dfx/profiling-config-naming.md +++ b/docs/dfx/profiling-config-naming.md @@ -87,7 +87,7 @@ by **independent layers**: | Compile-time (does the code exist?) | macros (`SIMPLER_DFX`, `SIMPLER_HOST_STRACE`, `SIMPLER_*_PROFILING`) | umbrella on, sub-tiers off | | Per-run (does this run collect X?) | `SIMPLER_DFX_FLAG_*` bitmask via `CallConfig` | none selected | | Runtime emission (does it actually emit?) | env (`SIMPLER_DEVICE_STRACE_ENABLE`, log level) | on | -| Runtime detail tier | `get_chip_swimlane_level()` | AICPU_TIMING | +| Runtime detail tier | `get_chip_swimlane_level()` | SCHEDULE_TIMING | `SIMPLER_HOST_STRACE` (compile) gates whether `[STRACE]` markers exist at all; `SIMPLER_DEVICE_STRACE_ENABLE` (runtime env) independently gates device-domain diff --git a/simpler_setup/tools/swimlane_converter.py b/simpler_setup/tools/swimlane_converter.py index 4fc9a8fa08..6ba0aad42b 100644 --- a/simpler_setup/tools/swimlane_converter.py +++ b/simpler_setup/tools/swimlane_converter.py @@ -283,7 +283,7 @@ def _decode_perf_data(data, *, timeline_origin_ns=None): # noqa: PLR0912, PLR09 the freq MUST come from the host, never be hardcoded here) - join `aicpu_tasks` by `(core_id, reg_task_id)`; unmatched rows are dropped and counted - - AICORE_TIMING (level=1): aicpu_tasks is empty by construction, so + - TASK_TIMING (level=1): aicpu_tasks is empty by construction, so synthesize one task per aicore record (dispatch/finish = 0) - sort joined `tasks` by `task_id` (= task_token_raw) - convert phase records from `*_cycles` → `*_time_us` @@ -511,7 +511,7 @@ def _core_type(core_id): } ) elif level == 1: - # AICORE_TIMING fallback: AICPU records are absent (complete_task + # TASK_TIMING fallback: AICPU records are absent (complete_task # bypassed). The AICore stream alone is the source of truth. for row in aicore_rows: core_id, task_token_raw, _reg_task_id, start_cycles, end_cycles, *rest = row @@ -734,7 +734,7 @@ def load_deps_kernel_map(deps_path): """Build a ``task_id → kernel_ids[3]`` map from deps.json's ``tasks[]``. a2a3 dep_gen captures per-task ``kernel_ids = [aic, aiv0, aiv1]`` so the - swimlane post-processor can resolve ``func_id`` at AICORE_TIMING (level=1) + swimlane post-processor can resolve ``func_id`` at TASK_TIMING (level=1) where the AICore record alone is on disk and carries ``func_id == -1``. The trace generator uses the per-record ``core_type`` to pick the right subslot: ``aic → kernel_ids[0]``, ``aiv → kernel_ids[1]`` (falling back @@ -943,7 +943,7 @@ def _append_dependency_flow_pair( # noqa: PLR0913 def resolve_func_id_from_kernel_map(task_id, core_type, kernel_map): - """Look up the active ``func_id`` for an AICORE_TIMING record via dep_gen. + """Look up the active ``func_id`` for a TASK_TIMING record via dep_gen. Picks the kernel_ids[3] subslot by record ``core_type``. Returns the resolved func_id (>= 0) on a hit, or -1 if no usable subslot was found @@ -1438,7 +1438,7 @@ def generate_chrome_trace_json( # noqa: PLR0912, PLR0913, PLR0915 if verbose: print(f" Unique cores: {len(unique_cores)}") - # Recover func_id for AICORE_TIMING (level=1) records, which the host + # Recover func_id for TASK_TIMING (level=1) records, which the host # emits as func_id=-1. Resolve once here against dep_gen's per-task # kernel_ids[3] (picking the subslot by core_type) and write it back onto # the task, so every downstream consumer — Worker View, Scheduler View, and @@ -3498,9 +3498,9 @@ def main(): deps_path = Path(args.deps_json) if args.deps_json else Path(input_path).parent / "deps.json" deps_edges = load_deps_json(deps_path) # Load the per-task kernel_ids map separately so the trace generator - # can resolve func_id=-1 records (AICORE_TIMING / level=1) back to + # can resolve func_id=-1 records (TASK_TIMING / level=1) back to # the real kernel name. Optional — pre-schema deps.json without - # kernel_ids and AICPU_TIMING+ runs both leave this at None. + # kernel_ids and SCHEDULE_TIMING+ runs both leave this at None. deps_kernel_map = load_deps_kernel_map(deps_path) deps_block_map = load_deps_block_map(deps_path) if deps_edges is not None: diff --git a/src/a2a3/runtime/host_build_graph/docs/profiling_levels.md b/src/a2a3/runtime/host_build_graph/docs/profiling_levels.md index 5bac75aa6d..897d7f9835 100644 --- a/src/a2a3/runtime/host_build_graph/docs/profiling_levels.md +++ b/src/a2a3/runtime/host_build_graph/docs/profiling_levels.md @@ -402,7 +402,7 @@ mirrors the PMU pattern — two independent channels (one binary, one int): (shared memory). Host writes it in `ChipSwimlaneCollector::initialize`; AICPU promotes it from the header in `chip_swimlane_aicpu_init` and exposes it via `get_chip_swimlane_level()` (typed `ChipSwimlaneLevel`) for - `>= AICPU_TIMING / SCHED_PHASES / ORCH_PHASES` gates. + `>= SCHEDULE_TIMING / SCHED_PHASES / ORCH_PHASES` gates. On sim, the binary on/off travels via the dlsym'd `set_chip_swimlane_enabled` entry point; the granular level still goes through the shared-memory @@ -412,7 +412,7 @@ header just like on onboard. | ----- | -------- | | 0 | Nothing (disabled) | | 1 | AICore timing only (start/end/task_token_raw) — AICPU `complete_task` is bypassed | -| 2 | + AICPU dispatch_time, finish_time | +| 2 | + Scheduler per-task dispatch_time, finish_time | | 3 | + Scheduler phases (`SCHED_*`) | | 4 | + Orchestrator phases (full) | @@ -458,10 +458,10 @@ content it depends on instead of relying on magic numbers: // Cheap binary check, available immediately after kernel entry. if (is_chip_swimlane_enabled()) { ... } -// AICPU dispatch/finish timestamps. +// Scheduler per-task dispatch/finish timestamps (AICPU-produced in this runtime). // Granular checks below require chip_swimlane_aicpu_init to have already run // (so the level has been promoted from the shared-memory header). -if (get_chip_swimlane_level() >= ChipSwimlaneLevel::AICPU_TIMING) { ... } +if (get_chip_swimlane_level() >= ChipSwimlaneLevel::SCHEDULE_TIMING) { ... } // Scheduler main-loop phase records (SCHED_*) if (get_chip_swimlane_level() >= ChipSwimlaneLevel::SCHED_PHASES) { ... } @@ -477,8 +477,8 @@ shared-memory field and mirrors `PmuEventType : uint32_t`): | Enumerator | Underlying value | | ---------- | ---------------- | | `DISABLED` | 0 | -| `AICORE_TIMING` | 1 | -| `AICPU_TIMING` | 2 | +| `TASK_TIMING` | 1 | +| `SCHEDULE_TIMING` | 2 | | `SCHED_PHASES` | 3 | | `ORCH_PHASES` | 4 | diff --git a/src/a2a3/runtime/host_build_graph/runtime/scheduler/scheduler_completion.cpp b/src/a2a3/runtime/host_build_graph/runtime/scheduler/scheduler_completion.cpp index 4fb8429f8f..4e170a00dc 100644 --- a/src/a2a3/runtime/host_build_graph/runtime/scheduler/scheduler_completion.cpp +++ b/src/a2a3/runtime/host_build_graph/runtime/scheduler/scheduler_completion.cpp @@ -200,14 +200,14 @@ void SchedulerContext::complete_slot_task( } #if SIMPLER_DFX - // Level gate: at AICORE_TIMING (level=1) the AICore record alone carries + // Level gate: at TASK_TIMING (level=1) the AICore record alone carries // {start, end, task_token_raw}, host resolves func_id/core_type from // dep_gen / per-core mapping, and AICPU has nothing to write. Only at - // AICPU_TIMING (level=2) and above does AICPU contribute dispatch/finish + // SCHEDULE_TIMING (level=2) and above does AICPU contribute dispatch/finish // timestamps via complete_task. Bypassing here saves the per-completion // hot-path cost (counter inc + ring lookup + record store + wmb + buffer // rotation bookkeeping) for runs that only want AICore timing. - if (chip_swimlane.chip_swimlane_enabled && chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane.chip_swimlane_enabled && chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { #if SIMPLER_SCHED_PROFILING uint64_t t_perf_start = get_sys_cnt_aicpu(); #endif @@ -340,7 +340,7 @@ void SchedulerContext::check_running_cores_for_completion( // charge AICPU completion-processing cost to the (end → finish) // span, masking the actual FIN-delivery latency. uint64_t finish_ts = 0; - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING && (t.pending_done || t.running_done)) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING && (t.pending_done || t.running_done)) { finish_ts = get_sys_cnt_aicpu(); } #endif @@ -542,7 +542,7 @@ SchedulerContext::SyncStartStageResult SchedulerContext::stage_sync_start_cores( sched_chip_swimlane_[thread_idx].sched_loop_count, static_cast(handle_count) ); } - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { dispatch_ts = pub_t0 != 0 ? pub_t0 : get_sys_cnt_aicpu(); } #endif diff --git a/src/a2a3/runtime/host_build_graph/runtime/scheduler/scheduler_context.h b/src/a2a3/runtime/host_build_graph/runtime/scheduler/scheduler_context.h index 6ea49fea24..a310a838d1 100644 --- a/src/a2a3/runtime/host_build_graph/runtime/scheduler/scheduler_context.h +++ b/src/a2a3/runtime/host_build_graph/runtime/scheduler/scheduler_context.h @@ -316,7 +316,7 @@ class SchedulerContext { // // dispatch_timestamp_slot points to the CoreExecState slot // (pending_dispatch_timestamp / running_dispatch_timestamp) selected at - // prepare time, or nullptr when chip swimlane is below AICPU_TIMING and no + // prepare time, or nullptr when chip swimlane is below SCHEDULE_TIMING and no // dispatch timestamp is being recorded. struct PublishHandle { uint64_t reg_addr; diff --git a/src/a2a3/runtime/host_build_graph/runtime/scheduler/scheduler_dispatch.cpp b/src/a2a3/runtime/host_build_graph/runtime/scheduler/scheduler_dispatch.cpp index e532c1b5d5..4b6cdb524b 100644 --- a/src/a2a3/runtime/host_build_graph/runtime/scheduler/scheduler_dispatch.cpp +++ b/src/a2a3/runtime/host_build_graph/runtime/scheduler/scheduler_dispatch.cpp @@ -237,7 +237,7 @@ SchedulerContext::PublishHandle SchedulerContext::prepare_subtask_to_core( // boundary. The completion-before-dispatch invariant makes this race-free // (all prior tasks on this core have FIN'd, so AICore has dcci'd their // records out of the old buffer). Gated on the same enable bit as flush - // so level=1 (AICORE_TIMING-only) participates without needing complete_task. + // so level=1 (TASK_TIMING-only) participates without needing complete_task. #if SIMPLER_DFX if (chip_swimlane_level_ != ChipSwimlaneLevel::DISABLED) { chip_swimlane_aicpu_on_aicore_dispatch(core_id, thread_idx, reg_task_id); @@ -246,7 +246,7 @@ SchedulerContext::PublishHandle SchedulerContext::prepare_subtask_to_core( uint64_t *dispatch_timestamp_slot = nullptr; #if SIMPLER_DFX - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { dispatch_timestamp_slot = to_pending ? &core_exec_state.pending_dispatch_timestamp : &core_exec_state.running_dispatch_timestamp; } @@ -401,7 +401,7 @@ void SchedulerContext::dispatch_shape( wmb(); uint64_t dispatch_ts = 0; #if SIMPLER_DFX - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { dispatch_ts = get_sys_cnt_aicpu(); } #endif diff --git a/src/a2a3/runtime/tensormap_and_ringbuffer/docs/profiling_levels.md b/src/a2a3/runtime/tensormap_and_ringbuffer/docs/profiling_levels.md index 4db296a856..811a1ce244 100644 --- a/src/a2a3/runtime/tensormap_and_ringbuffer/docs/profiling_levels.md +++ b/src/a2a3/runtime/tensormap_and_ringbuffer/docs/profiling_levels.md @@ -226,7 +226,7 @@ mirrors the PMU pattern — two independent channels (one binary, one int): (shared memory). Host writes it in `ChipSwimlaneCollector::initialize`; AICPU promotes it from the header in `chip_swimlane_aicpu_init` and exposes it via `get_chip_swimlane_level()` (typed `ChipSwimlaneLevel`) for - `>= AICPU_TIMING / SCHED_PHASES / ORCH_PHASES` gates. + `>= SCHEDULE_TIMING / SCHED_PHASES / ORCH_PHASES` gates. On sim, the binary on/off travels via the dlsym'd `set_chip_swimlane_enabled` entry point; the granular level still goes through the shared-memory @@ -236,7 +236,7 @@ header just like on onboard. | ----- | -------- | | 0 | Nothing (disabled) | | 1 | AICore timing only (start/end/task_token_raw) — AICPU `complete_task` is bypassed | -| 2 | + dispatch_time, finish_time | +| 2 | + Scheduler per-task dispatch_time, finish_time | | 3 | + Scheduler phases (`SCHED_*`) | | 4 | + Orchestrator phases (full) | @@ -286,10 +286,10 @@ content it depends on instead of relying on magic numbers: // Cheap binary check, available immediately after kernel entry. if (is_chip_swimlane_enabled()) { ... } -// AICPU dispatch/finish timestamps. +// Scheduler per-task dispatch/finish timestamps (AICPU-produced in this runtime). // Granular checks below require chip_swimlane_aicpu_init to have already run // (so the level has been promoted from the shared-memory header). -if (get_chip_swimlane_level() >= ChipSwimlaneLevel::AICPU_TIMING) { ... } +if (get_chip_swimlane_level() >= ChipSwimlaneLevel::SCHEDULE_TIMING) { ... } // Scheduler main-loop phase records (SCHED_*) if (get_chip_swimlane_level() >= ChipSwimlaneLevel::SCHED_PHASES) { ... } @@ -305,8 +305,8 @@ shared-memory field and mirrors `PmuEventType : uint32_t`): | Enumerator | Underlying value | | ---------- | ---------------- | | `DISABLED` | 0 | -| `AICORE_TIMING` | 1 | -| `AICPU_TIMING` | 2 | +| `TASK_TIMING` | 1 | +| `SCHEDULE_TIMING` | 2 | | `SCHED_PHASES` | 3 | | `ORCH_PHASES` | 4 | diff --git a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_completion.cpp b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_completion.cpp index 872810fef4..a25c1aecb1 100644 --- a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_completion.cpp +++ b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_completion.cpp @@ -247,14 +247,14 @@ void SchedulerContext::complete_slot_task( } #if SIMPLER_DFX - // Level gate: at AICORE_TIMING (level=1) the AICore record alone carries + // Level gate: at TASK_TIMING (level=1) the AICore record alone carries // {start, end, task_token_raw}, host resolves func_id/core_type from // dep_gen / per-core mapping, and AICPU has nothing to write. Only at - // AICPU_TIMING (level=2) and above does AICPU contribute dispatch/finish + // SCHEDULE_TIMING (level=2) and above does AICPU contribute dispatch/finish // timestamps via complete_task. Bypassing here saves the per-completion // hot-path cost (counter inc + ring lookup + record store + wmb + buffer // rotation bookkeeping) for runs that only want AICore timing. - if (chip_swimlane.chip_swimlane_enabled && chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane.chip_swimlane_enabled && chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { #if SIMPLER_SCHED_PROFILING uint64_t t_perf_start = get_sys_cnt_aicpu(); #endif @@ -391,7 +391,7 @@ void SchedulerContext::check_running_cores_for_completion( // charge AICPU completion-processing cost to the (end → finish) // span, masking the actual FIN-delivery latency. uint64_t finish_ts = 0; - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING && (t.pending_done || t.running_done)) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING && (t.pending_done || t.running_done)) { finish_ts = get_sys_cnt_aicpu(); } #endif @@ -604,7 +604,7 @@ SchedulerContext::SyncStartStageResult SchedulerContext::stage_sync_start_cores( sched_chip_swimlane_[thread_idx].sched_loop_count, static_cast(handle_count) ); } - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { dispatch_ts = pub_t0 != 0 ? pub_t0 : get_sys_cnt_aicpu(); } #endif diff --git a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_context.h b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_context.h index b6192bd9c0..930fc21481 100644 --- a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_context.h +++ b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_context.h @@ -264,7 +264,7 @@ class SchedulerContext { // // dispatch_timestamp_slot points to the CoreExecState slot // (pending_dispatch_timestamp / running_dispatch_timestamp) selected at - // prepare time, or nullptr when chip swimlane is below AICPU_TIMING and no + // prepare time, or nullptr when chip swimlane is below SCHEDULE_TIMING and no // dispatch timestamp is being recorded. struct PublishHandle { uint64_t reg_addr; diff --git a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_dispatch.cpp b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_dispatch.cpp index f770bce9a3..bf8bc75733 100644 --- a/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_dispatch.cpp +++ b/src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_dispatch.cpp @@ -216,7 +216,7 @@ SchedulerContext::PublishHandle SchedulerContext::prepare_subtask_to_core( // released once AICore ACKs this boundary dispatch (see the ACK hook in // check_running_cores_for_completion), because FIN precedes the swimlane // record on this runtime. `reg_task_id` is passed as that ACK gate. Gated on - // the same enable bit as flush so level=1 (AICORE_TIMING-only) participates. + // the same enable bit as flush so level=1 (TASK_TIMING-only) participates. #if SIMPLER_DFX if (chip_swimlane_level_ != ChipSwimlaneLevel::DISABLED) { chip_swimlane_aicpu_on_aicore_dispatch(core_id, thread_idx, reg_task_id); @@ -225,7 +225,7 @@ SchedulerContext::PublishHandle SchedulerContext::prepare_subtask_to_core( uint64_t *dispatch_timestamp_slot = nullptr; #if SIMPLER_DFX - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { dispatch_timestamp_slot = to_pending ? &core_exec_state.pending_dispatch_timestamp : &core_exec_state.running_dispatch_timestamp; } @@ -378,7 +378,7 @@ void SchedulerContext::dispatch_shape( wmb(); uint64_t dispatch_ts = 0; #if SIMPLER_DFX - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { dispatch_ts = get_sys_cnt_aicpu(); } #endif diff --git a/src/a5/runtime/host_build_graph/aicore/aicore_executor.cpp b/src/a5/runtime/host_build_graph/aicore/aicore_executor.cpp index 2315e7df62..0062b5e9ad 100644 --- a/src/a5/runtime/host_build_graph/aicore/aicore_executor.cpp +++ b/src/a5/runtime/host_build_graph/aicore/aicore_executor.cpp @@ -249,33 +249,33 @@ __aicore__ __attribute__((always_inline)) void commit_task_timing_trace( } __aicore__ bool bootstrap_ready_graph( - const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *resolver, - __gm__ SchedulerRunControl *run_control, uint64_t resolver_count, SchedulerWorkerStats *stats, bool trace_enabled, + const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *scheduler, + __gm__ SchedulerRunControl *run_control, uint64_t scheduler_count, SchedulerWorkerStats *stats, bool trace_enabled, SchedulerDeferredAivQueue *deferred_aiv, __gm__ SchedulerReadyOwnerState *ready_owner ) { - if (resolver_count == 0 || resolver->inbox_index >= resolver_count || ready_owner == nullptr) return false; + if (scheduler_count == 0 || scheduler->inbox_index >= scheduler_count || ready_owner == nullptr) return false; if (trace_enabled) stats->bootstrap_start_cycles = scheduler_cycles(); SchedulerReadyBatch batches[SCHEDULER_CORE_TYPE_COUNT]{}; - uint64_t tasks_per_resolver = graph.task_count / resolver_count; - uint64_t remainder = graph.task_count % resolver_count; - uint64_t task_begin = resolver->inbox_index * tasks_per_resolver + - (resolver->inbox_index < remainder ? resolver->inbox_index : remainder); - uint64_t task_end = task_begin + tasks_per_resolver + (resolver->inbox_index < remainder ? 1 : 0); + uint64_t tasks_per_scheduler = graph.task_count / scheduler_count; + uint64_t remainder = graph.task_count % scheduler_count; + uint64_t task_begin = scheduler->inbox_index * tasks_per_scheduler + + (scheduler->inbox_index < remainder ? scheduler->inbox_index : remainder); + uint64_t task_end = task_begin + tasks_per_scheduler + (scheduler->inbox_index < remainder ? 1 : 0); for (uint64_t task_id = task_begin; task_id < task_end; ++task_id) { __gm__ SchedulerTaskMetadata *metadata = - scheduler_task_metadata_at(scheduler_state_base, resolver, static_cast(task_id)); + scheduler_task_metadata_at(scheduler_state_base, scheduler, static_cast(task_id)); scheduler_observe_cache_line(metadata); if (!scheduler_task_is_executable(metadata->flags)) continue; SchedulerRouteResult route = scheduler_task_has_fanin(metadata->flags) ? scheduler_bootstrap_route_task( - graph, scheduler_state_base, resolver, run_control, static_cast(task_id), &stats->wake + graph, scheduler_state_base, scheduler, run_control, static_cast(task_id), &stats->wake ) : SchedulerRouteResult::READY_TO_ENQUEUE; if (route == SchedulerRouteResult::ERROR) return false; if (route == SchedulerRouteResult::READY_TO_ENQUEUE && !scheduler_bootstrap_ready_batch_append( - scheduler_state_base, resolver, static_cast(task_id), + scheduler_state_base, scheduler, static_cast(task_id), &batches[scheduler_metadata_core_type_index(scheduler_metadata_single_subtask_slot(metadata ->active_mask))], &stats->ready, trace_enabled @@ -288,17 +288,18 @@ __aicore__ bool bootstrap_ready_graph( uint64_t ready_types = 0; for (uint32_t type = 0; type < SCHEDULER_CORE_TYPE_COUNT; ++type) { if (!scheduler_bootstrap_ready_batch_publish( - scheduler_state_base, resolver, type, resolver->inbox_index, &batches[type], &stats->ready, &ready_types + scheduler_state_base, scheduler, type, scheduler->inbox_index, &batches[type], &stats->ready, + &ready_types )) return false; } - __gm__ SchedulerReadyDirectory *ready_directory = scheduler_ready_directory_at(scheduler_state_base, resolver); - scheduler_gm_store(ready_directory->bootstrap_ready_types[resolver->inbox_index], ready_types); + __gm__ SchedulerReadyDirectory *ready_directory = scheduler_ready_directory_at(scheduler_state_base, scheduler); + scheduler_gm_store(ready_directory->bootstrap_ready_types[scheduler->inbox_index], ready_types); if (trace_enabled) stats->bootstrap_scan_end_cycles = scheduler_cycles(); uint64_t arrived = scheduler_gm_fetch_add(run_control->bootstrap_scan_arrived_count, UINT64_C(1)) + 1; - if (arrived == resolver_count) { - scheduler_bootstrap_ready_directory_publish(scheduler_state_base, resolver, resolver_count); + if (arrived == scheduler_count) { + scheduler_bootstrap_ready_directory_publish(scheduler_state_base, scheduler, scheduler_count); scheduler_gm_publish(run_control->bootstrap_scan_complete, UINT64_C(1)); } else { uint32_t barrier_backoff = kInitialBackoffIterations; @@ -309,7 +310,7 @@ __aicore__ bool bootstrap_ready_graph( barrier_start, scheduler_cycles(), scheduler_gm_query(run_control->scheduler_timeout_cycles) )) { scheduler_record_error( - run_control, SCHEDULER_TASK_ID_INVALID, SchedulerGraphResult::TIMEOUT, &graph, resolver, + run_control, SCHEDULER_TASK_ID_INVALID, SchedulerGraphResult::TIMEOUT, &graph, scheduler, SchedulerErrorSite::BOOTSTRAP_SCAN_TIMEOUT ); return false; @@ -326,16 +327,16 @@ __aicore__ bool bootstrap_ready_graph( if (trace_enabled) stats->target_bootstrap_start_cycles = scheduler_cycles(); for (uint32_t cluster_lane = 0; cluster_lane < PLATFORM_CORES_PER_BLOCKDIM; ++cluster_lane) { - const uint64_t worker_id = resolver->cluster_worker_ids[cluster_lane]; + const uint64_t worker_id = scheduler->cluster_worker_ids[cluster_lane]; uint64_t target_start = trace_enabled ? scheduler_cycles() : 0; - __gm__ SchedulerWorkerContext *target = scheduler_worker_context_at(scheduler_state_base, resolver, worker_id); + __gm__ SchedulerWorkerContext *target = scheduler_worker_context_at(scheduler_state_base, scheduler, worker_id); scheduler_observe_cache_line(target); scheduler_observe_cache_line(&target->task_metadata_offset); if (target->active == 0) continue; uint32_t type = scheduler_core_type_index(target->core_type); for (uint32_t slot_index = 0; slot_index < SCHEDULER_PENDING_SLOT_COUNT; ++slot_index) { __gm__ SchedulerDispatchSlot *slot = - scheduler_dispatch_slot_at(scheduler_state_base, resolver, worker_id, slot_index); + scheduler_dispatch_slot_at(scheduler_state_base, scheduler, worker_id, slot_index); scheduler_observe_cache_line(slot); scheduler_initialize_free_slot(slot); } @@ -347,29 +348,29 @@ __aicore__ bool bootstrap_ready_graph( // Prepare the first executable wave while the sole DMB launch gate is // still closed. uint64_t ready_victim_cursors[SCHEDULER_CORE_TYPE_COUNT]{ - (resolver->inbox_index + 1) % resolver_count, - (resolver->inbox_index + 1) % resolver_count, + (scheduler->inbox_index + 1) % scheduler_count, + (scheduler->inbox_index + 1) % scheduler_count, }; bool fill_failed = false; (void)scheduler_fill_cluster_normal_slots( - graph, scheduler_state_base, resolver, run_control, ready_victim_cursors, &stats->ready, trace_enabled, 0, + graph, scheduler_state_base, scheduler, run_control, ready_victim_cursors, &stats->ready, trace_enabled, 0, nullptr, deferred_aiv, ready_owner, &fill_failed ); if (fill_failed) return false; // No peer can make progress while the launch gate is closed. Materialize - // every Resolver reservation now so local deferred state and FILLING slots + // every Scheduler reservation now so local deferred state and FILLING slots // never cross the bootstrap boundary. while (deferred_aiv != nullptr && deferred_aiv->count != 0) { - if (!scheduler_publish_deferred_aiv_to_resolver( - graph, scheduler_state_base, resolver, run_control, deferred_aiv, trace_enabled, nullptr + if (!scheduler_publish_deferred_aiv_local( + graph, scheduler_state_base, scheduler, run_control, deferred_aiv, trace_enabled, nullptr )) return false; } // This completion publication is observed by AICPU before it emits the - // one and only DMB release. Resolvers do not wait on another barrier. + // one and only DMB release. Schedulers do not wait on another barrier. arrived = scheduler_gm_fetch_add(run_control->bootstrap_arrived_count, UINT64_C(1)) + 1; - if (arrived == resolver_count) scheduler_gm_publish(run_control->bootstrap_complete, UINT64_C(1)); + if (arrived == scheduler_count) scheduler_gm_publish(run_control->bootstrap_complete, UINT64_C(1)); if (trace_enabled) stats->bootstrap_end_cycles = scheduler_cycles(); return true; } @@ -380,16 +381,16 @@ __aicore__ bool run_ready_dispatch_loop( uint64_t aicore_entry_cycles, uint64_t handshake_publish_cycles, uint64_t register_release_cycles, uint64_t descriptor_cache_observed_cycles, SchedulerDeferredAivQueue *deferred_aiv ) { - uint64_t resolver_count = scheduler_gm_query(run_control->resolver_count); - bool resolver_worker = context->is_resolver != 0; - if (resolver_count == 0) return false; + uint64_t scheduler_count = scheduler_gm_query(run_control->scheduler_count); + bool scheduler_worker = context->is_scheduler != 0; + if (scheduler_count == 0) return false; __gm__ SchedulerReadyOwnerState *ready_owner = nullptr; - if (resolver_worker) { + if (scheduler_worker) { ready_owner = scheduler_ready_owner_state_at(scheduler_state_base, context); } uint64_t ready_victim_cursors[SCHEDULER_CORE_TYPE_COUNT]{ - resolver_worker ? (context->inbox_index + 1) % resolver_count : 0, - resolver_worker ? (context->inbox_index + 1) % resolver_count : 0, + scheduler_worker ? (context->inbox_index + 1) % scheduler_count : 0, + scheduler_worker ? (context->inbox_index + 1) % scheduler_count : 0, }; uint64_t seen_publication[SCHEDULER_PENDING_SLOT_COUNT]{}; uint64_t previous_trace_commit_end = 0; @@ -414,7 +415,7 @@ __aicore__ bool run_ready_dispatch_loop( } bool scheduler_progress = false; - if (resolver_worker && !scheduler_ready_owner_maintain(scheduler_state_base, context, ready_owner)) { + if (scheduler_worker && !scheduler_ready_owner_maintain(scheduler_state_base, context, ready_owner)) { scheduler_record_error( run_control, SCHEDULER_TASK_ID_INVALID, SchedulerGraphResult::INVALID_ARGUMENTS, &graph, context, SchedulerErrorSite::READY_OWNER_MAINTENANCE_FAILED @@ -422,7 +423,7 @@ __aicore__ bool run_ready_dispatch_loop( return false; } uint32_t preferred_ready_slot = UINT32_MAX; - if (resolver_worker && deferred_aiv != nullptr && deferred_aiv->count != 0) { + if (scheduler_worker && deferred_aiv != nullptr && deferred_aiv->count != 0) { const uint32_t deferred_before = deferred_aiv->count; if (!scheduler_drain_deferred_aiv_to_peer( graph, scheduler_state_base, context, run_control, deferred_aiv, &stats->wake, &stats->ready, @@ -430,13 +431,13 @@ __aicore__ bool run_ready_dispatch_loop( )) return false; scheduler_progress = deferred_aiv->count != deferred_before; - if (deferred_aiv->count != 0 && !scheduler_publish_deferred_aiv_to_resolver( + if (deferred_aiv->count != 0 && !scheduler_publish_deferred_aiv_local( graph, scheduler_state_base, context, run_control, deferred_aiv, trace_enabled, &preferred_ready_slot )) return false; } - if (resolver_worker && preferred_ready_slot == UINT32_MAX) { + if (scheduler_worker && preferred_ready_slot == UINT32_MAX) { uint64_t operation_start = trace_enabled ? get_sys_cnt_aicore() : 0; uint64_t direct_refilled_slot_mask = 0; SchedulerCompletionServiceTiming completion_timing{}; @@ -489,7 +490,7 @@ __aicore__ bool run_ready_dispatch_loop( )) return false; scheduler_progress = scheduler_progress || deferred_aiv->count != deferred_before; - if (deferred_aiv->count != 0 && !scheduler_publish_deferred_aiv_to_resolver( + if (deferred_aiv->count != 0 && !scheduler_publish_deferred_aiv_local( graph, scheduler_state_base, context, run_control, deferred_aiv, trace_enabled, &preferred_ready_slot )) @@ -589,7 +590,7 @@ __aicore__ bool run_ready_dispatch_loop( uint64_t completion_start = get_sys_cnt_aicore(); uint64_t local_completion_index = stats->completion.enqueue_count; uint64_t completion_id = scheduler_completion_id(context, local_completion_index); - uint64_t completion_inbox_index = context->resolver_index; + uint64_t completion_inbox_index = context->scheduler_index; __gm__ SchedulerCompletionInbox *completion_line = scheduler_completion_inbox_at(scheduler_state_base, context, context->worker_index); scheduler_gm_store(completion_line->completed_generations[slot_index], slot->generation); @@ -720,13 +721,13 @@ __aicore__ __attribute__((weak)) void aicore_execute(__gm__ Runtime *runtime, in if (context->active != 0) { scheduler_observe_data_cache(reinterpret_cast<__gm__ void *>(graph.storage_address)); descriptor_cache_observed_cycles = trace_enabled ? get_sys_cnt_aicore() : 0; - if (context->is_resolver != 0) { + if (context->is_scheduler != 0) { ready_owner = scheduler_ready_owner_state_at(scheduler_state_base, context); scheduler_ready_owner_init(ready_owner); } - if (context->is_resolver != 0 && + if (context->is_scheduler != 0 && !bootstrap_ready_graph( - graph, scheduler_state_base, context, run_control, scheduler_gm_query(run_control->resolver_count), + graph, scheduler_state_base, context, run_control, scheduler_gm_query(run_control->scheduler_count), &stats, trace_enabled, &deferred_aiv, ready_owner )) { scheduler_record_error( diff --git a/src/a5/runtime/host_build_graph/aicpu/aicore_lifecycle.cpp b/src/a5/runtime/host_build_graph/aicpu/aicore_lifecycle.cpp index 296e35c423..0f85302c1a 100644 --- a/src/a5/runtime/host_build_graph/aicpu/aicore_lifecycle.cpp +++ b/src/a5/runtime/host_build_graph/aicpu/aicore_lifecycle.cpp @@ -71,8 +71,8 @@ int32_t AicoreLifecycle::pre_handshake_init(Runtime *runtime, int32_t aicpu_thre const bool chip_swimlane_enabled = is_chip_swimlane_enabled(); if (chip_swimlane_enabled || is_pmu_enabled() || is_dump_args_enabled()) { LOG_WARN( - "A5 HBG resident AICore diagnostics are best-effort: artifacts may be absent or incomplete and do not " - "yet describe Resolver scheduling" + "A5 HBG AICore Scheduler diagnostics are best-effort: artifacts may be absent or incomplete and do not " + "yet describe Scheduler scheduling" ); } if (chip_swimlane_enabled) chip_swimlane_aicpu_init(core_count_); @@ -125,7 +125,7 @@ void AicoreLifecycle::handshake_partition(Runtime *runtime, int32_t tidx, int32_ }; } if (scheduler_watchdog_expired(wait_start, get_sys_cnt_aicpu(), timeout_cycles)) { - LOG_ERROR("A5 HBG resident scheduler handshake timeout thread=%d remaining=%d", tidx, remaining); + LOG_ERROR("A5 HBG AICore Scheduler handshake timeout thread=%d remaining=%d", tidx, remaining); record_lifecycle_timeout(runtime, SchedulerErrorSite::AICPU_HANDSHAKE_TIMEOUT); handshake_failed_.store(true, std::memory_order_release); return; @@ -214,8 +214,8 @@ int32_t AicoreLifecycle::post_handshake_init(Runtime *runtime) { } cluster_workers[cluster][lane] = worker; } - static_assert(PLATFORM_CORES_PER_BLOCKDIM == 3, "Resolver selection assumes one AIC and two AIV lanes"); - static_assert(PLATFORM_AIV_CORES_PER_BLOCKDIM == 2, "Resolver selection assumes two AIV lanes per cluster"); + static_assert(PLATFORM_CORES_PER_BLOCKDIM == 3, "Scheduler selection assumes one AIC and two AIV lanes"); + static_assert(PLATFORM_AIV_CORES_PER_BLOCKDIM == 2, "Scheduler selection assumes two AIV lanes per cluster"); for (int32_t cluster = 0; cluster < aic_count; ++cluster) { for (int32_t lane = 0; lane < PLATFORM_CORES_PER_BLOCKDIM; ++lane) { if (cluster_workers[cluster][lane] < 0) { @@ -252,27 +252,27 @@ int32_t AicoreLifecycle::post_handshake_init(Runtime *runtime) { for (int32_t cluster = 0; cluster < aic_count; ++cluster) { const int32_t aiv0_worker = cluster_workers[cluster][1]; const int32_t aiv1_worker = cluster_workers[cluster][2]; - // Do not bind Resolver ownership to a runtime worker rank. Pick the + // Do not bind Scheduler ownership to a runtime worker rank. Pick the // lower physical AIV in each discovered hardware Cluster. - const uint64_t resolver_worker = static_cast( + const uint64_t scheduler_worker = static_cast( cores_[aiv0_worker].physical_core_id <= cores_[aiv1_worker].physical_core_id ? aiv0_worker : aiv1_worker ); for (int32_t lane = 0; lane < PLATFORM_CORES_PER_BLOCKDIM; ++lane) { const int32_t worker = cluster_workers[cluster][lane]; - const bool resolver_lane = static_cast(worker) == resolver_worker; + const bool scheduler_lane = static_cast(worker) == scheduler_worker; const bool additional_aiv_lane = - lane != 0 && !resolver_lane && cluster * PLATFORM_AIV_CORES_PER_BLOCKDIM + 1 < requested_aiv; + lane != 0 && !scheduler_lane && cluster * PLATFORM_AIV_CORES_PER_BLOCKDIM + 1 < requested_aiv; const bool active_lane = cluster < active_clusters && - (resolver_lane || (lane == 0 && cluster < requested_aic) || additional_aiv_lane); + (scheduler_lane || (lane == 0 && cluster < requested_aic) || additional_aiv_lane); contexts[worker].active = active_lane ? 1 : 0; contexts[worker].cluster_count = static_cast(active_clusters); contexts[worker].cluster_index = static_cast(cluster); - contexts[worker].resolver_index = cluster < active_clusters ? static_cast(cluster) : UINT64_MAX; - contexts[worker].resolver_worker_id = resolver_worker; - contexts[worker].is_resolver = resolver_lane && cluster < active_clusters ? 1 : 0; + contexts[worker].scheduler_index = cluster < active_clusters ? static_cast(cluster) : UINT64_MAX; + contexts[worker].scheduler_worker_id = scheduler_worker; + contexts[worker].is_scheduler = scheduler_lane && cluster < active_clusters ? 1 : 0; contexts[worker].inbox_index = - contexts[worker].is_resolver != 0 ? static_cast(cluster) : UINT64_MAX; - contexts[worker].resolver_count = static_cast(active_clusters); + contexts[worker].is_scheduler != 0 ? static_cast(cluster) : UINT64_MAX; + contexts[worker].scheduler_count = static_cast(active_clusters); for (int32_t member = 0; member < PLATFORM_CORES_PER_BLOCKDIM; ++member) contexts[worker].cluster_worker_ids[member] = static_cast(cluster_workers[cluster][member]); } @@ -287,7 +287,7 @@ int32_t AicoreLifecycle::post_handshake_init(Runtime *runtime) { run_control->active_worker_count = static_cast(active_aic) + static_cast(active_aiv); run_control->aic_active_worker_count = static_cast(active_aic); run_control->aiv_active_worker_count = static_cast(active_aiv); - run_control->resolver_count = static_cast(active_clusters); + run_control->scheduler_count = static_cast(active_clusters); run_control->scheduler_timeout_cycles = resident_scheduler_timeout_cycles(); if (executable_task_count == 0) { run_control->bootstrap_scan_arrived_count = static_cast(active_clusters); @@ -355,7 +355,7 @@ int32_t AicoreLifecycle::wait_bootstrap_complete(Runtime *runtime) { if (run_control->scheduler_error != 0) return -1; } if (scheduler_watchdog_expired(watchdog_start, get_sys_cnt_aicpu(), timeout_cycles)) { - LOG_ERROR("%s", "A5 HBG resident scheduler bootstrap timeout"); + LOG_ERROR("%s", "A5 HBG AICore Scheduler bootstrap timeout"); record_lifecycle_timeout(runtime, SchedulerErrorSite::BOOTSTRAP_COMPLETE_TIMEOUT); return -1; } diff --git a/src/a5/runtime/host_build_graph/aicpu/aicpu_executor.cpp b/src/a5/runtime/host_build_graph/aicpu/aicpu_executor.cpp index bdb0bc0fe0..8c94a03e24 100644 --- a/src/a5/runtime/host_build_graph/aicpu/aicpu_executor.cpp +++ b/src/a5/runtime/host_build_graph/aicpu/aicpu_executor.cpp @@ -117,7 +117,7 @@ static void record_resident_timeout(Runtime *runtime, SchedulerErrorSite error_s &header->sched_error_code, static_cast(SchedulerGraphResult::TIMEOUT) ); } - LOG_ERROR("A5 HBG resident scheduler timeout site=%" PRIu64, static_cast(error_site)); + LOG_ERROR("A5 HBG AICore Scheduler timeout site=%" PRIu64, static_cast(error_site)); } static int32_t read_runtime_status(Runtime *runtime) { diff --git a/src/a5/runtime/host_build_graph/docs/profiling_levels.md b/src/a5/runtime/host_build_graph/docs/profiling_levels.md index 4a19611367..3afd5471ae 100644 --- a/src/a5/runtime/host_build_graph/docs/profiling_levels.md +++ b/src/a5/runtime/host_build_graph/docs/profiling_levels.md @@ -7,11 +7,11 @@ This document describes the profiling macro hierarchy and logging control in the The runtime uses a hierarchical profiling system with compile-time macros to control profiling code compilation and log output. The `enable_chip_swimlane` runtime flag (integer perf_level 0–4) controls data collection granularity (performance buffers, shared memory writes) but does NOT control log output. > **A5 HBG scheduler selection.** Diagnostic flags never select the scheduler. -> Ordinary DAGs remain on the resident AICore scheduler, while Graph replay -> remains on its explicit legacy compatibility path. Until resident Resolver +> Ordinary DAGs remain on the A5 HBG AICore Scheduler, while Graph replay +> remains on its explicit legacy compatibility path. Until AICore Scheduler > profiling lands, chip-swimlane, PMU, and argument-dump collection for > ordinary DAGs is best-effort; artifacts may be absent or incomplete and must -> not be used as evidence of Resolver scheduling behavior or as a profiling-on +> not be used as evidence of AICore Scheduler behavior or as a profiling-on > performance baseline. > **host_build_graph (host-orch) note.** The profiling **macros** below > (`SIMPLER_DFX`, `SIMPLER_ORCH_PROFILING`, …) are shared with @@ -409,7 +409,7 @@ mirrors the PMU pattern — two independent channels (one binary, one int): (shared memory). Host writes it in `ChipSwimlaneCollector::initialize`; AICPU promotes it from the header in `chip_swimlane_aicpu_init` and exposes it via `get_chip_swimlane_level()` (typed `ChipSwimlaneLevel`) for - `>= AICPU_TIMING / SCHED_PHASES / ORCH_PHASES` gates. + `>= SCHEDULE_TIMING / SCHED_PHASES / ORCH_PHASES` gates. On sim, the binary on/off travels via the dlsym'd `set_chip_swimlane_enabled` entry point; the granular level still goes through the shared-memory @@ -419,7 +419,7 @@ header just like on onboard. | ----- | -------- | | 0 | Nothing (disabled) | | 1 | AICore timing only (start/end/task_token_raw) — AICPU `complete_task` is bypassed | -| 2 | + AICPU dispatch_time, finish_time | +| 2 | + Scheduler per-task dispatch_time, finish_time | | 3 | + Scheduler phases (`SCHED_*`) | | 4 | + Orchestrator phases (full) | @@ -465,10 +465,11 @@ content it depends on instead of relying on magic numbers: // Cheap binary check, available immediately after kernel entry. if (is_chip_swimlane_enabled()) { ... } -// AICPU dispatch/finish timestamps. +// On the AICPU compatibility path, gate its Scheduler task-timing producer. +// The A5 HBG AICore Scheduler applies the same enum contract host-side. // Granular checks below require chip_swimlane_aicpu_init to have already run // (so the level has been promoted from the shared-memory header). -if (get_chip_swimlane_level() >= ChipSwimlaneLevel::AICPU_TIMING) { ... } +if (get_chip_swimlane_level() >= ChipSwimlaneLevel::SCHEDULE_TIMING) { ... } // Scheduler main-loop phase records (SCHED_*) if (get_chip_swimlane_level() >= ChipSwimlaneLevel::SCHED_PHASES) { ... } @@ -484,8 +485,8 @@ shared-memory field and mirrors `PmuEventType : uint32_t`): | Enumerator | Underlying value | | ---------- | ---------------- | | `DISABLED` | 0 | -| `AICORE_TIMING` | 1 | -| `AICPU_TIMING` | 2 | +| `TASK_TIMING` | 1 | +| `SCHEDULE_TIMING` | 2 | | `SCHED_PHASES` | 3 | | `ORCH_PHASES` | 4 | diff --git a/src/a5/runtime/host_build_graph/host/runtime_maker.cpp b/src/a5/runtime/host_build_graph/host/runtime_maker.cpp index aa5822d781..1b8c22da37 100644 --- a/src/a5/runtime/host_build_graph/host/runtime_maker.cpp +++ b/src/a5/runtime/host_build_graph/host/runtime_maker.cpp @@ -934,7 +934,7 @@ bool create_scheduler_state( runtime, SchedulerStateOwner{allocation, reinterpret_cast(aligned_address), allocation_size, layout} ); } - LOG_INFO("A5 HBG: selected resident AICore scheduling for %d tasks", total_tasks); + LOG_INFO("A5 HBG: selected AICore Scheduler for %d tasks", total_tasks); return true; } diff --git a/src/a5/runtime/host_build_graph/runtime/dispatch_payload.h b/src/a5/runtime/host_build_graph/runtime/dispatch_payload.h index b0c4e9f909..795c49afc9 100644 --- a/src/a5/runtime/host_build_graph/runtime/dispatch_payload.h +++ b/src/a5/runtime/host_build_graph/runtime/dispatch_payload.h @@ -119,7 +119,7 @@ struct alignas(64) DispatchPayload { uint64_t args[DISPATCH_MAX_ARGS]; /** Per-dispatch global context: sub_block_id identifies the selected AIV - * task subslot. The resident scheduler writes it during materialization; + * task subslot. The AICore Scheduler writes it during materialization; * the legacy scheduler seeds the equivalent per-core value at startup. * args[SPMD_GLOBAL_CONTEXT_INDEX] points here. */ GlobalContext global_context; diff --git a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_completion.cpp b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_completion.cpp index c89d62cab1..4e458b372d 100644 --- a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_completion.cpp +++ b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_completion.cpp @@ -200,14 +200,14 @@ void SchedulerContext::complete_slot_task( } #if SIMPLER_DFX - // Level gate: at AICORE_TIMING (level=1) the AICore record alone carries + // Level gate: at TASK_TIMING (level=1) the AICore record alone carries // {start, end, task_token_raw}, host resolves func_id/core_type from // dep_gen / per-core mapping, and AICPU has nothing to write. Only at - // AICPU_TIMING (level=2) and above does AICPU contribute dispatch/finish + // SCHEDULE_TIMING (level=2) and above does AICPU contribute dispatch/finish // timestamps via complete_task. Bypassing here saves the per-completion // hot-path cost (counter inc + ring lookup + record store + wmb + buffer // rotation bookkeeping) for runs that only want AICore timing. - if (chip_swimlane.chip_swimlane_enabled && chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane.chip_swimlane_enabled && chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { #if SIMPLER_SCHED_PROFILING uint64_t t_perf_start = get_sys_cnt_aicpu(); #endif @@ -345,7 +345,7 @@ void SchedulerContext::check_running_cores_for_completion( // charge AICPU completion-processing cost to the (end → finish) // span, masking the actual FIN-delivery latency. uint64_t finish_ts = 0; - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING && (t.pending_done || t.running_done)) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING && (t.pending_done || t.running_done)) { finish_ts = get_sys_cnt_aicpu(); } #endif @@ -547,7 +547,7 @@ SchedulerContext::SyncStartStageResult SchedulerContext::stage_sync_start_cores( sched_chip_swimlane_[thread_idx].sched_loop_count, static_cast(handle_count) ); } - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { dispatch_ts = pub_t0 != 0 ? pub_t0 : get_sys_cnt_aicpu(); } #endif diff --git a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_completion.h b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_completion.h index cd7fdb864f..dddc6e6c58 100644 --- a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_completion.h +++ b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_completion.h @@ -16,7 +16,7 @@ #include "scheduler_ready.h" inline __aicore__ bool scheduler_service_cluster_completion_slot( - const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *resolver, + const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *scheduler, __gm__ SchedulerRunControl *run_control, uint32_t cluster_lane, uint32_t pending_slot, uint32_t completed_generation, SchedulerWakeStats *wake_stats, SchedulerReadyStats *ready_stats, SchedulerCompletionStats *completion_stats, uint64_t *ready_victim_cursors, bool trace_enabled, @@ -27,19 +27,19 @@ inline __aicore__ bool scheduler_service_cluster_completion_slot( if (cluster_lane >= PLATFORM_CORES_PER_BLOCKDIM || pending_slot >= SCHEDULER_PENDING_SLOT_COUNT || completed_generation == 0) return false; - const uint64_t worker_id = resolver->cluster_worker_ids[cluster_lane]; - if (worker_id >= resolver->runtime_worker_count) return false; - __gm__ SchedulerWorkerContext *target = scheduler_worker_context_at(scheduler_state_base, resolver, worker_id); + const uint64_t worker_id = scheduler->cluster_worker_ids[cluster_lane]; + if (worker_id >= scheduler->runtime_worker_count) return false; + __gm__ SchedulerWorkerContext *target = scheduler_worker_context_at(scheduler_state_base, scheduler, worker_id); if (target->active == 0) return false; __gm__ SchedulerCompletionInbox *completion_line = - scheduler_completion_inbox_at(scheduler_state_base, resolver, worker_id); + scheduler_completion_inbox_at(scheduler_state_base, scheduler, worker_id); __gm__ SchedulerDispatchSlot *slot = - scheduler_dispatch_slot_at(scheduler_state_base, resolver, worker_id, pending_slot); + scheduler_dispatch_slot_at(scheduler_state_base, scheduler, worker_id, pending_slot); const uint64_t publication = scheduler_gm_query(slot->publication); if (scheduler_dispatch_state(publication) != SchedulerDispatchSlotState::READY || scheduler_dispatch_generation(publication) != completed_generation) { scheduler_record_error( - run_control, slot->task_id, SchedulerGraphResult::INVALID_ARGUMENTS, &graph, resolver, + run_control, slot->task_id, SchedulerGraphResult::INVALID_ARGUMENTS, &graph, scheduler, SchedulerErrorSite::COMPLETION_GENERATION_MISMATCH ); return false; @@ -51,7 +51,7 @@ inline __aicore__ bool scheduler_service_cluster_completion_slot( const int64_t task_id = slot->task_id; if (slot->gang != 0) { scheduler_record_error( - run_control, task_id, SchedulerGraphResult::UNSUPPORTED_SHAPE, &graph, resolver, + run_control, task_id, SchedulerGraphResult::UNSUPPORTED_SHAPE, &graph, scheduler, SchedulerErrorSite::COMPLETION_UNEXPECTED_GANG_SLOT ); return false; @@ -67,10 +67,10 @@ inline __aicore__ bool scheduler_service_cluster_completion_slot( uint64_t refill_start_cycles = 0; uint64_t refill_end_cycles = 0; bool refilled = false; - __gm__ SchedulerTaskControl *control = scheduler_task_control_at(scheduler_state_base, resolver, task_id); + __gm__ SchedulerTaskControl *control = scheduler_task_control_at(scheduler_state_base, scheduler, task_id); scheduler_gm_store(control->state, static_cast(SchedulerTaskState::DONE)); if (!scheduler_resolve_completion( - graph, scheduler_state_base, resolver, run_control, task_id, wake_stats, ready_stats, completion_stats, + graph, scheduler_state_base, scheduler, run_control, task_id, wake_stats, ready_stats, completion_stats, owner_state, trace_enabled, false, timing == nullptr ? nullptr : &ready_publish_cycles )) return false; @@ -86,12 +86,12 @@ inline __aicore__ bool scheduler_service_cluster_completion_slot( bool ready_available = replacement_ready != nullptr; if (ready_available) { ready = *replacement_ready; - } else if (ready_victim_cursors != nullptr && worker_id != resolver->worker_index) { - // A normal AIV task is never refilled directly onto the Resolver. + } else if (ready_victim_cursors != nullptr && worker_id != scheduler->worker_index) { + // A normal AIV task is never refilled directly onto the Scheduler. // Its completed slot becomes capacity for late binding instead. const uint32_t core_type = scheduler_metadata_core_type_index(completed_subtask_slot); if (!scheduler_claim_ready_for_slot( - graph, scheduler_state_base, resolver, run_control, resolver->resolver_count, core_type, + graph, scheduler_state_base, scheduler, run_control, scheduler->scheduler_count, core_type, &ready_victim_cursors[core_type], ready_stats, &ready, owner_state, trace_enabled )) return false; @@ -100,7 +100,7 @@ inline __aicore__ bool scheduler_service_cluster_completion_slot( if (ready_available) { SchedulerFreeSlotClaim claim{worker_id, pending_slot, slot->generation}; if (!scheduler_fill_dispatch_slot( - graph, scheduler_state_base, resolver, run_control, claim, ready, trace_enabled + graph, scheduler_state_base, scheduler, run_control, claim, ready, trace_enabled )) return false; refilled = true; @@ -136,22 +136,22 @@ inline __aicore__ uint32_t scheduler_completion_catchup_mask(uint32_t initial_co } inline __aicore__ bool scheduler_service_cluster_completions( - const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *resolver, + const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *scheduler, __gm__ SchedulerRunControl *run_control, SchedulerWakeStats *wake_stats, SchedulerReadyStats *ready_stats, SchedulerCompletionStats *completion_stats, uint64_t *ready_victim_cursors = nullptr, bool trace_enabled = false, uint64_t *direct_refilled_slot_mask = nullptr, SchedulerCompletionServiceTiming *timing = nullptr, __gm__ SchedulerReadyOwnerState *owner_state = nullptr ) { - if (resolver->is_resolver == 0) return false; + if (scheduler->is_scheduler == 0) return false; if (direct_refilled_slot_mask != nullptr) *direct_refilled_slot_mask = 0; bool progress = false; for (uint32_t cluster_lane = 0; cluster_lane < PLATFORM_CORES_PER_BLOCKDIM; ++cluster_lane) { - const uint64_t worker_id = resolver->cluster_worker_ids[cluster_lane]; - if (worker_id >= resolver->runtime_worker_count) continue; - __gm__ SchedulerWorkerContext *target = scheduler_worker_context_at(scheduler_state_base, resolver, worker_id); + const uint64_t worker_id = scheduler->cluster_worker_ids[cluster_lane]; + if (worker_id >= scheduler->runtime_worker_count) continue; + __gm__ SchedulerWorkerContext *target = scheduler_worker_context_at(scheduler_state_base, scheduler, worker_id); if (target->active == 0) continue; __gm__ SchedulerCompletionInbox *completion_line = - scheduler_completion_inbox_at(scheduler_state_base, resolver, worker_id); + scheduler_completion_inbox_at(scheduler_state_base, scheduler, worker_id); uint64_t completed_generations = scheduler_gm_query_u32_pair(completion_line->completed_generations); uint32_t initial_completion_mask = 0; for (uint32_t pending_slot = 0; pending_slot < SCHEDULER_PENDING_SLOT_COUNT; ++pending_slot) { @@ -169,7 +169,7 @@ inline __aicore__ bool scheduler_service_cluster_completions( if (completed_generation == 0) continue; bool direct_refilled = false; if (!scheduler_service_cluster_completion_slot( - graph, scheduler_state_base, resolver, run_control, cluster_lane, pending_slot, + graph, scheduler_state_base, scheduler, run_control, cluster_lane, pending_slot, completed_generation, wake_stats, ready_stats, completion_stats, ready_victim_cursors, trace_enabled, nullptr, &direct_refilled, timing, owner_state )) diff --git a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_context.h b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_context.h index 6ea49fea24..a310a838d1 100644 --- a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_context.h +++ b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_context.h @@ -316,7 +316,7 @@ class SchedulerContext { // // dispatch_timestamp_slot points to the CoreExecState slot // (pending_dispatch_timestamp / running_dispatch_timestamp) selected at - // prepare time, or nullptr when chip swimlane is below AICPU_TIMING and no + // prepare time, or nullptr when chip swimlane is below SCHEDULE_TIMING and no // dispatch timestamp is being recorded. struct PublishHandle { uint64_t reg_addr; diff --git a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_dispatch.cpp b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_dispatch.cpp index 92846dc54e..56dded2b7d 100644 --- a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_dispatch.cpp +++ b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_dispatch.cpp @@ -238,7 +238,7 @@ SchedulerContext::PublishHandle SchedulerContext::prepare_subtask_to_core( // boundary. The completion-before-dispatch invariant makes this race-free // (all prior tasks on this core have FIN'd, so AICore has dcci'd their // records out of the old buffer). Gated on the same enable bit as flush - // so level=1 (AICORE_TIMING-only) participates without needing complete_task. + // so level=1 (TASK_TIMING-only) participates without needing complete_task. #if SIMPLER_DFX if (chip_swimlane_level_ != ChipSwimlaneLevel::DISABLED) { chip_swimlane_aicpu_on_aicore_dispatch(core_id, thread_idx, reg_task_id); @@ -247,7 +247,7 @@ SchedulerContext::PublishHandle SchedulerContext::prepare_subtask_to_core( uint64_t *dispatch_timestamp_slot = nullptr; #if SIMPLER_DFX - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { dispatch_timestamp_slot = to_pending ? &core_exec_state.pending_dispatch_timestamp : &core_exec_state.running_dispatch_timestamp; } @@ -402,7 +402,7 @@ void SchedulerContext::dispatch_shape( wmb(); uint64_t dispatch_ts = 0; #if SIMPLER_DFX - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { dispatch_ts = get_sys_cnt_aicpu(); } #endif diff --git a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_dispatch.h b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_dispatch.h index 9d7bbdcd51..db6cd4da56 100644 --- a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_dispatch.h +++ b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_dispatch.h @@ -21,7 +21,7 @@ struct SchedulerDeferredAivDispatch { }; struct SchedulerDeferredAivQueue { - // Every entry owns one Resolver slot held in FILLING, so a peer miss can + // Every entry owns one Scheduler slot held in FILLING, so a peer miss can // always fall back to local execution without another capacity decision. SchedulerDeferredAivDispatch entries[SCHEDULER_PENDING_SLOT_COUNT]{}; uint32_t count{0}; @@ -59,49 +59,51 @@ inline __aicore__ void scheduler_finish_normal_dispatch_stage( } inline __aicore__ bool scheduler_normal_aiv_worker_precedes( - uint32_t candidate_occupied_slots, bool candidate_is_resolver, uint32_t selected_occupied_slots, - bool selected_is_resolver + uint32_t candidate_occupied_slots, bool candidate_is_scheduler, uint32_t selected_occupied_slots, + bool selected_is_scheduler ) { - if (candidate_is_resolver != selected_is_resolver) return !candidate_is_resolver; + if (candidate_is_scheduler != selected_is_scheduler) return !candidate_is_scheduler; return candidate_occupied_slots < selected_occupied_slots; } // The return value reports whether this pass made progress; failed independently reports an aborted pass. inline __aicore__ bool scheduler_fill_cluster_normal_slots( - const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *resolver, + const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *scheduler, __gm__ SchedulerRunControl *run_control, uint64_t *ready_victim_cursors, SchedulerReadyStats *ready_stats, bool trace_enabled, uint64_t skip_slot_mask = 0, SchedulerNormalDispatchTiming *timing = nullptr, SchedulerDeferredAivQueue *deferred_aiv = nullptr, __gm__ SchedulerReadyOwnerState *owner_state = nullptr, bool *failed = nullptr ) { if (failed != nullptr) *failed = false; - if (resolver->is_resolver == 0) return false; + if (scheduler->is_scheduler == 0) return false; const uint32_t aic_core_type = static_cast(CoreType::AIC); uint64_t stage_start = timing == nullptr ? 0 : scheduler_cycles(); uint64_t detail_start = timing == nullptr ? 0 : scheduler_normal_dispatch_detail_cycles(*timing, aic_core_type); bool progress = false; // AIC has no peer lane in its Cluster, so preserve the existing slot order. - if (scheduler_ready_directory_nonempty(scheduler_state_base, resolver, resolver->resolver_count, aic_core_type)) { + if (scheduler_ready_directory_nonempty( + scheduler_state_base, scheduler, scheduler->scheduler_count, aic_core_type + )) { bool aic_ready_available = true; for (uint32_t cluster_lane = 0; cluster_lane < PLATFORM_CORES_PER_BLOCKDIM && aic_ready_available; ++cluster_lane) { - const uint64_t worker_id = resolver->cluster_worker_ids[cluster_lane]; - if (worker_id >= resolver->runtime_worker_count) continue; + const uint64_t worker_id = scheduler->cluster_worker_ids[cluster_lane]; + if (worker_id >= scheduler->runtime_worker_count) continue; __gm__ SchedulerWorkerContext *target = - scheduler_worker_context_at(scheduler_state_base, resolver, worker_id); + scheduler_worker_context_at(scheduler_state_base, scheduler, worker_id); if (target->active == 0 || target->core_type != static_cast(CoreType::AIC)) continue; for (uint32_t pending_slot = 0; pending_slot < SCHEDULER_PENDING_SLOT_COUNT; ++pending_slot) { if ((skip_slot_mask & (UINT64_C(1) << (cluster_lane * SCHEDULER_PENDING_SLOT_COUNT + pending_slot))) != 0) continue; __gm__ SchedulerDispatchSlot *slot = - scheduler_dispatch_slot_at(scheduler_state_base, resolver, worker_id, pending_slot); + scheduler_dispatch_slot_at(scheduler_state_base, scheduler, worker_id, pending_slot); const uint64_t publication = scheduler_gm_query(slot->publication); if (scheduler_dispatch_state(publication) != SchedulerDispatchSlotState::FREE) continue; SchedulerReadyClaim ready{}; if (!scheduler_claim_ready_for_slot( - graph, scheduler_state_base, resolver, run_control, resolver->resolver_count, aic_core_type, + graph, scheduler_state_base, scheduler, run_control, scheduler->scheduler_count, aic_core_type, &ready_victim_cursors[aic_core_type], ready_stats, &ready, owner_state, trace_enabled )) { if (failed != nullptr) *failed = true; @@ -124,7 +126,7 @@ inline __aicore__ bool scheduler_fill_cluster_normal_slots( ); SchedulerDispatchFillTiming fill_timing{}; if (!scheduler_fill_dispatch_slot( - graph, scheduler_state_base, resolver, run_control, claim, ready, trace_enabled, + graph, scheduler_state_base, scheduler, run_control, claim, ready, trace_enabled, timing == nullptr ? nullptr : &fill_timing )) { if (failed != nullptr) *failed = true; @@ -141,32 +143,34 @@ inline __aicore__ bool scheduler_fill_cluster_normal_slots( } scheduler_finish_normal_dispatch_stage(timing, aic_core_type, stage_start, detail_start); - // A Resolver shares its AIV with Executor work. Exhaust the non-Resolver + // A Scheduler shares its AIV with Executor work. Exhaust the non-Scheduler // peer's free slots first, then claim more work only against reserved - // Resolver capacity. The caller decides the reserved work's owner after + // Scheduler capacity. The caller decides the reserved work's owner after // the rest of this scheduling round completes. struct AivWorkerSlots { uint64_t worker_id{UINT64_MAX}; uint64_t publications[SCHEDULER_PENDING_SLOT_COUNT]{}; uint32_t free_mask{0}; uint32_t occupied_slots{0}; - bool is_resolver{false}; + bool is_scheduler{false}; }; const uint32_t aiv_core_type = static_cast(CoreType::AIV); stage_start = timing == nullptr ? 0 : scheduler_cycles(); detail_start = timing == nullptr ? 0 : scheduler_normal_dispatch_detail_cycles(*timing, aiv_core_type); - if (scheduler_ready_directory_nonempty(scheduler_state_base, resolver, resolver->resolver_count, aiv_core_type)) { + if (scheduler_ready_directory_nonempty( + scheduler_state_base, scheduler, scheduler->scheduler_count, aiv_core_type + )) { AivWorkerSlots aiv_workers[PLATFORM_AIV_CORES_PER_BLOCKDIM]{}; uint32_t aiv_worker_count = 0; for (uint32_t cluster_lane = 0; cluster_lane < PLATFORM_CORES_PER_BLOCKDIM; ++cluster_lane) { - const uint64_t worker_id = resolver->cluster_worker_ids[cluster_lane]; - if (worker_id >= resolver->runtime_worker_count) continue; + const uint64_t worker_id = scheduler->cluster_worker_ids[cluster_lane]; + if (worker_id >= scheduler->runtime_worker_count) continue; __gm__ SchedulerWorkerContext *target = - scheduler_worker_context_at(scheduler_state_base, resolver, worker_id); + scheduler_worker_context_at(scheduler_state_base, scheduler, worker_id); if (target->active == 0 || target->core_type != static_cast(CoreType::AIV)) continue; if (aiv_worker_count >= PLATFORM_AIV_CORES_PER_BLOCKDIM) { scheduler_record_error( - run_control, SCHEDULER_TASK_ID_INVALID, SchedulerGraphResult::INVALID_ARGUMENTS, &graph, resolver, + run_control, SCHEDULER_TASK_ID_INVALID, SchedulerGraphResult::INVALID_ARGUMENTS, &graph, scheduler, SchedulerErrorSite::NORMAL_DISPATCH_INVALID_TOPOLOGY ); if (failed != nullptr) *failed = true; @@ -174,7 +178,7 @@ inline __aicore__ bool scheduler_fill_cluster_normal_slots( } AivWorkerSlots &worker = aiv_workers[aiv_worker_count++]; worker.worker_id = worker_id; - worker.is_resolver = worker_id == resolver->worker_index; + worker.is_scheduler = worker_id == scheduler->worker_index; for (uint32_t pending_slot = 0; pending_slot < SCHEDULER_PENDING_SLOT_COUNT; ++pending_slot) { if ((skip_slot_mask & (UINT64_C(1) << (cluster_lane * SCHEDULER_PENDING_SLOT_COUNT + pending_slot))) != 0) { @@ -182,7 +186,7 @@ inline __aicore__ bool scheduler_fill_cluster_normal_slots( continue; } __gm__ SchedulerDispatchSlot *slot = - scheduler_dispatch_slot_at(scheduler_state_base, resolver, worker_id, pending_slot); + scheduler_dispatch_slot_at(scheduler_state_base, scheduler, worker_id, pending_slot); const uint64_t publication = scheduler_gm_query(slot->publication); worker.publications[pending_slot] = publication; if (scheduler_dispatch_state(publication) == SchedulerDispatchSlotState::FREE) @@ -197,8 +201,8 @@ inline __aicore__ bool scheduler_fill_cluster_normal_slots( if (candidate.free_mask == 0) continue; if (selected == aiv_worker_count || scheduler_normal_aiv_worker_precedes( - candidate.occupied_slots, candidate.is_resolver, aiv_workers[selected].occupied_slots, - aiv_workers[selected].is_resolver + candidate.occupied_slots, candidate.is_scheduler, aiv_workers[selected].occupied_slots, + aiv_workers[selected].is_scheduler )) selected = worker_index; } @@ -206,7 +210,7 @@ inline __aicore__ bool scheduler_fill_cluster_normal_slots( AivWorkerSlots &worker = aiv_workers[selected]; const uint32_t pending_slot = static_cast(__builtin_ctz(worker.free_mask)); worker.free_mask &= ~(1U << pending_slot); - if (worker.is_resolver && (deferred_aiv == nullptr || deferred_aiv->count >= SCHEDULER_PENDING_SLOT_COUNT)) + if (worker.is_scheduler && (deferred_aiv == nullptr || deferred_aiv->count >= SCHEDULER_PENDING_SLOT_COUNT)) break; const uint64_t publication = worker.publications[pending_slot]; SchedulerFreeSlotClaim claim{ @@ -215,13 +219,13 @@ inline __aicore__ bool scheduler_fill_cluster_normal_slots( scheduler_dispatch_generation(publication), }; __gm__ SchedulerDispatchSlot *slot = - scheduler_dispatch_slot_at(scheduler_state_base, resolver, worker.worker_id, pending_slot); + scheduler_dispatch_slot_at(scheduler_state_base, scheduler, worker.worker_id, pending_slot); scheduler_gm_store( slot->publication, scheduler_dispatch_publication(claim.generation, SchedulerDispatchSlotState::FILLING) ); SchedulerReadyClaim ready{}; if (!scheduler_claim_ready_for_slot( - graph, scheduler_state_base, resolver, run_control, resolver->resolver_count, aiv_core_type, + graph, scheduler_state_base, scheduler, run_control, scheduler->scheduler_count, aiv_core_type, &ready_victim_cursors[aiv_core_type], ready_stats, &ready, owner_state, trace_enabled )) { scheduler_gm_store( @@ -240,7 +244,7 @@ inline __aicore__ bool scheduler_fill_cluster_normal_slots( ); break; } - if (worker.is_resolver) { + if (worker.is_scheduler) { deferred_aiv->entries[deferred_aiv->count++] = {ready, claim}; ++worker.occupied_slots; progress = true; @@ -248,7 +252,7 @@ inline __aicore__ bool scheduler_fill_cluster_normal_slots( } SchedulerDispatchFillTiming fill_timing{}; if (!scheduler_fill_dispatch_slot( - graph, scheduler_state_base, resolver, run_control, claim, ready, trace_enabled, + graph, scheduler_state_base, scheduler, run_control, claim, ready, trace_enabled, timing == nullptr ? nullptr : &fill_timing )) { if (failed != nullptr) *failed = true; @@ -268,24 +272,24 @@ inline __aicore__ bool scheduler_fill_cluster_normal_slots( } inline __aicore__ bool scheduler_release_deferred_aiv_reservation( - const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *resolver, + const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *scheduler, __gm__ SchedulerRunControl *run_control, const SchedulerFreeSlotClaim &reservation ) { - if (reservation.worker_id != resolver->worker_index || reservation.slot_index >= SCHEDULER_PENDING_SLOT_COUNT) { + if (reservation.worker_id != scheduler->worker_index || reservation.slot_index >= SCHEDULER_PENDING_SLOT_COUNT) { scheduler_record_error( - run_control, SCHEDULER_TASK_ID_INVALID, SchedulerGraphResult::INVALID_ARGUMENTS, &graph, resolver, + run_control, SCHEDULER_TASK_ID_INVALID, SchedulerGraphResult::INVALID_ARGUMENTS, &graph, scheduler, SchedulerErrorSite::DEFERRED_RESERVATION_INVALID_OWNER ); return false; } __gm__ SchedulerDispatchSlot *slot = - scheduler_dispatch_slot_at(scheduler_state_base, resolver, reservation.worker_id, reservation.slot_index); + scheduler_dispatch_slot_at(scheduler_state_base, scheduler, reservation.worker_id, reservation.slot_index); const uint64_t publication = scheduler_gm_query(slot->publication); if (scheduler_dispatch_state(publication) != SchedulerDispatchSlotState::FILLING || scheduler_dispatch_generation(publication) != reservation.generation || slot->task_id != SCHEDULER_TASK_ID_INVALID) { scheduler_record_error( - run_control, slot->task_id, SchedulerGraphResult::INVALID_ARGUMENTS, &graph, resolver, + run_control, slot->task_id, SchedulerGraphResult::INVALID_ARGUMENTS, &graph, scheduler, SchedulerErrorSite::DEFERRED_RESERVATION_INVALID_STATE ); return false; @@ -297,12 +301,12 @@ inline __aicore__ bool scheduler_release_deferred_aiv_reservation( } inline __aicore__ int32_t -scheduler_deferred_aiv_peer_lane(__gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *resolver) { +scheduler_deferred_aiv_peer_lane(__gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *scheduler) { for (uint32_t cluster_lane = 0; cluster_lane < PLATFORM_CORES_PER_BLOCKDIM; ++cluster_lane) { - const uint64_t worker_id = resolver->cluster_worker_ids[cluster_lane]; - if (worker_id >= resolver->runtime_worker_count) continue; - if (worker_id == resolver->worker_index) continue; - __gm__ SchedulerWorkerContext *target = scheduler_worker_context_at(scheduler_state_base, resolver, worker_id); + const uint64_t worker_id = scheduler->cluster_worker_ids[cluster_lane]; + if (worker_id >= scheduler->runtime_worker_count) continue; + if (worker_id == scheduler->worker_index) continue; + __gm__ SchedulerWorkerContext *target = scheduler_worker_context_at(scheduler_state_base, scheduler, worker_id); scheduler_observe_cache_line(target); if (target->active != 0 && target->core_type == static_cast(CoreType::AIV)) return static_cast(cluster_lane); @@ -311,21 +315,21 @@ scheduler_deferred_aiv_peer_lane(__gm__ void *scheduler_state_base, __gm__ Sched } inline __aicore__ bool scheduler_drain_deferred_aiv_to_peer( - const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *resolver, + const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *scheduler, __gm__ SchedulerRunControl *run_control, SchedulerDeferredAivQueue *queue, SchedulerWakeStats *wake_stats, SchedulerReadyStats *ready_stats, SchedulerCompletionStats *completion_stats, bool trace_enabled, SchedulerCompletionServiceTiming *completion_timing = nullptr, SchedulerNormalDispatchTiming *dispatch_timing = nullptr, __gm__ SchedulerReadyOwnerState *owner_state = nullptr ) { if (queue == nullptr || queue->count == 0) return true; - const int32_t peer_lane = scheduler_deferred_aiv_peer_lane(scheduler_state_base, resolver); - // A Resolver may be the only active AIV in its Cluster (for example, a + const int32_t peer_lane = scheduler_deferred_aiv_peer_lane(scheduler_state_base, scheduler); + // A Scheduler may be the only active AIV in its Cluster (for example, a // single-root AIV graph). There is then nothing to drain to; leave the - // reservation queued so the caller can publish it on the Resolver itself. + // reservation queued so the caller can publish it on the Scheduler itself. if (peer_lane < 0) return true; - const uint64_t peer_worker_id = resolver->cluster_worker_ids[static_cast(peer_lane)]; + const uint64_t peer_worker_id = scheduler->cluster_worker_ids[static_cast(peer_lane)]; __gm__ SchedulerCompletionInbox *completion_line = - scheduler_completion_inbox_at(scheduler_state_base, resolver, peer_worker_id); + scheduler_completion_inbox_at(scheduler_state_base, scheduler, peer_worker_id); const uint32_t aiv_core_type = static_cast(CoreType::AIV); for (uint32_t pass = 0; pass < 2 && queue->count != 0; ++pass) { @@ -334,7 +338,7 @@ inline __aicore__ bool scheduler_drain_deferred_aiv_to_peer( for (uint32_t pending_slot = 0; pending_slot < SCHEDULER_PENDING_SLOT_COUNT && queue->count != 0; ++pending_slot) { __gm__ SchedulerDispatchSlot *peer_slot = - scheduler_dispatch_slot_at(scheduler_state_base, resolver, peer_worker_id, pending_slot); + scheduler_dispatch_slot_at(scheduler_state_base, scheduler, peer_worker_id, pending_slot); const uint64_t publication = scheduler_gm_query(peer_slot->publication); const SchedulerDispatchSlotState state = scheduler_dispatch_state(publication); const uint32_t generation = scheduler_dispatch_generation(publication); @@ -347,7 +351,7 @@ inline __aicore__ bool scheduler_drain_deferred_aiv_to_peer( ); SchedulerDispatchFillTiming fill_timing{}; if (!scheduler_fill_dispatch_slot( - graph, scheduler_state_base, resolver, run_control, + graph, scheduler_state_base, scheduler, run_control, SchedulerFreeSlotClaim{peer_worker_id, pending_slot, generation}, queue->entries[0].ready, trace_enabled, dispatch_timing == nullptr ? nullptr : &fill_timing )) @@ -366,7 +370,7 @@ inline __aicore__ bool scheduler_drain_deferred_aiv_to_peer( scheduler_observe_cache_line(peer_slot); if (peer_slot->gang != 0) continue; if (!scheduler_service_cluster_completion_slot( - graph, scheduler_state_base, resolver, run_control, static_cast(peer_lane), + graph, scheduler_state_base, scheduler, run_control, static_cast(peer_lane), pending_slot, completed_generation, wake_stats, ready_stats, completion_stats, nullptr, trace_enabled, &queue->entries[0].ready, &refilled, completion_timing, owner_state ) || @@ -374,7 +378,7 @@ inline __aicore__ bool scheduler_drain_deferred_aiv_to_peer( return false; } if (!scheduler_release_deferred_aiv_reservation( - graph, scheduler_state_base, resolver, run_control, queue->entries[0].reserved_slot + graph, scheduler_state_base, scheduler, run_control, queue->entries[0].reserved_slot )) return false; scheduler_deferred_aiv_pop_front(queue); @@ -383,24 +387,24 @@ inline __aicore__ bool scheduler_drain_deferred_aiv_to_peer( return true; } -inline __aicore__ bool scheduler_publish_deferred_aiv_to_resolver( - const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *resolver, +inline __aicore__ bool scheduler_publish_deferred_aiv_local( + const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *scheduler, __gm__ SchedulerRunControl *run_control, SchedulerDeferredAivQueue *queue, bool trace_enabled, uint32_t *published_slot, SchedulerNormalDispatchTiming *timing = nullptr ) { if (published_slot != nullptr) *published_slot = UINT32_MAX; if (queue == nullptr || queue->count == 0) return true; const SchedulerDeferredAivDispatch &entry = queue->entries[0]; - if (entry.reserved_slot.worker_id != resolver->worker_index || + if (entry.reserved_slot.worker_id != scheduler->worker_index || entry.reserved_slot.slot_index >= SCHEDULER_PENDING_SLOT_COUNT) { scheduler_record_error( - run_control, entry.ready.task_id, SchedulerGraphResult::INVALID_ARGUMENTS, &graph, resolver, + run_control, entry.ready.task_id, SchedulerGraphResult::INVALID_ARGUMENTS, &graph, scheduler, SchedulerErrorSite::DEFERRED_PUBLISH_INVALID_RESERVATION ); return false; } __gm__ SchedulerDispatchSlot *slot = scheduler_dispatch_slot_at( - scheduler_state_base, resolver, entry.reserved_slot.worker_id, entry.reserved_slot.slot_index + scheduler_state_base, scheduler, entry.reserved_slot.worker_id, entry.reserved_slot.slot_index ); const uint64_t publication = scheduler_gm_query(slot->publication); scheduler_observe_cache_line(slot); @@ -408,7 +412,7 @@ inline __aicore__ bool scheduler_publish_deferred_aiv_to_resolver( scheduler_dispatch_generation(publication) != entry.reserved_slot.generation || slot->task_id != SCHEDULER_TASK_ID_INVALID) { scheduler_record_error( - run_control, entry.ready.task_id, SchedulerGraphResult::INVALID_ARGUMENTS, &graph, resolver, + run_control, entry.ready.task_id, SchedulerGraphResult::INVALID_ARGUMENTS, &graph, scheduler, SchedulerErrorSite::DEFERRED_PUBLISH_INVALID_RESERVATION ); return false; @@ -416,7 +420,7 @@ inline __aicore__ bool scheduler_publish_deferred_aiv_to_resolver( const uint32_t aiv_core_type = static_cast(CoreType::AIV); SchedulerDispatchFillTiming fill_timing{}; if (!scheduler_fill_dispatch_slot( - graph, scheduler_state_base, resolver, run_control, entry.reserved_slot, entry.ready, trace_enabled, + graph, scheduler_state_base, scheduler, run_control, entry.reserved_slot, entry.ready, trace_enabled, timing == nullptr ? nullptr : &fill_timing )) return false; diff --git a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_ready.h b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_ready.h index 8118a07cc2..264e51fc4f 100644 --- a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_ready.h +++ b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_ready.h @@ -491,7 +491,7 @@ inline __aicore__ bool scheduler_bootstrap_ready_batch_publish( uint64_t inbox_index, SchedulerReadyBatch *batch, SchedulerReadyStats *stats, uint64_t *ready_types ) { if (batch == nullptr || batch->head == SCHEDULER_INBOX_EMPTY) return true; - if (core_type_index >= SCHEDULER_CORE_TYPE_COUNT || inbox_index >= SCHEDULER_RESOLVER_CAPACITY || batch->tail < 0 || + if (core_type_index >= SCHEDULER_CORE_TYPE_COUNT || inbox_index >= SCHEDULER_CAPACITY || batch->tail < 0 || ready_types == nullptr) return false; __gm__ SchedulerReadyInbox *inbox = @@ -505,23 +505,22 @@ inline __aicore__ bool scheduler_bootstrap_ready_batch_publish( } inline __aicore__ bool scheduler_bootstrap_ready_directory_publish( - __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *context, uint64_t resolver_count + __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *context, uint64_t scheduler_count ) { - if (resolver_count == 0 || resolver_count > SCHEDULER_RESOLVER_CAPACITY) return false; + if (scheduler_count == 0 || scheduler_count > SCHEDULER_CAPACITY) return false; __gm__ SchedulerReadyDirectory *directory = scheduler_ready_directory_at(scheduler_state_base, context); - for (uint64_t inbox_index = 0; inbox_index < resolver_count; inbox_index += 8) + for (uint64_t inbox_index = 0; inbox_index < scheduler_count; inbox_index += 8) scheduler_invalidate_cache_line(&directory->bootstrap_ready_types[inbox_index]); scheduler_cache_barrier(); uint32_t shard_count = static_cast( - (resolver_count + SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD - 1) / - SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD + (scheduler_count + SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD - 1) / SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD ); for (uint32_t type = 0; type < SCHEDULER_CORE_TYPE_COUNT; ++type) { for (uint32_t shard = 0; shard < shard_count; ++shard) { uint64_t bits = 0; - uint64_t shard_begin = static_cast(shard) * SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD; - uint64_t shard_end = shard_begin + SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD; - if (shard_end > resolver_count) shard_end = resolver_count; + uint64_t shard_begin = static_cast(shard) * SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD; + uint64_t shard_end = shard_begin + SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD; + if (shard_end > scheduler_count) shard_end = scheduler_count; for (uint64_t inbox_index = shard_begin; inbox_index < shard_end; ++inbox_index) { uint64_t ready_types = directory->bootstrap_ready_types[inbox_index]; if ((ready_types & (UINT64_C(1) << type)) != 0) bits |= UINT64_C(1) << (inbox_index - shard_begin); @@ -568,16 +567,16 @@ inline __aicore__ bool scheduler_ready_batch_append( inline __aicore__ void scheduler_ready_directory_set( __gm__ SchedulerReadyDirectory *directory, uint32_t core_type_index, uint64_t inbox_index ) { - uint64_t shard = inbox_index / SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD; - uint64_t bit = UINT64_C(1) << (inbox_index % SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD); + uint64_t shard = inbox_index / SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD; + uint64_t bit = UINT64_C(1) << (inbox_index % SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD); scheduler_gm_fetch_or(directory->core_types[core_type_index][shard].bits, bit); } inline __aicore__ void scheduler_ready_directory_clear( __gm__ SchedulerReadyDirectory *directory, uint32_t core_type_index, uint64_t inbox_index ) { - uint64_t shard = inbox_index / SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD; - uint64_t bit = UINT64_C(1) << (inbox_index % SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD); + uint64_t shard = inbox_index / SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD; + uint64_t bit = UINT64_C(1) << (inbox_index % SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD); scheduler_gm_fetch_and(directory->core_types[core_type_index][shard].bits, ~bit); } @@ -633,7 +632,7 @@ inline __aicore__ bool scheduler_ready_owner_maintain_type( __gm__ SchedulerReadyOwnerState *owner_state ) { if (owner_state == nullptr || core_type_index >= SCHEDULER_CORE_TYPE_COUNT || - context->inbox_index >= SCHEDULER_RESOLVER_CAPACITY) + context->inbox_index >= SCHEDULER_CAPACITY) return false; __gm__ SchedulerReadyOwnerQueue *owner_queue = &owner_state->queues[core_type_index]; __gm__ SchedulerReadyInbox *inbox = @@ -688,7 +687,7 @@ inline __aicore__ bool scheduler_ready_batch_push( __gm__ SchedulerReadyOwnerState *owner_state ) { if (batch == nullptr || owner_state == nullptr || core_type_index >= SCHEDULER_CORE_TYPE_COUNT || - inbox_index >= SCHEDULER_RESOLVER_CAPACITY || inbox_index != context->inbox_index) + inbox_index >= SCHEDULER_CAPACITY || inbox_index != context->inbox_index) return false; if (batch->head == SCHEDULER_INBOX_EMPTY) return true; if (batch->tail < 0) return false; @@ -783,12 +782,12 @@ inline __aicore__ bool scheduler_ready_pop_from_inbox( } inline __aicore__ uint64_t scheduler_load_ready_directory_shard( - __gm__ SchedulerReadyDirectory *directory, uint64_t resolver_count, uint32_t core_type_index, uint64_t inbox_index + __gm__ SchedulerReadyDirectory *directory, uint64_t scheduler_count, uint32_t core_type_index, uint64_t inbox_index ) { - uint64_t shard = inbox_index / SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD; - uint64_t shard_begin = shard * SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD; - uint64_t shard_end = shard_begin + SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD; - if (shard_end > resolver_count) shard_end = resolver_count; + uint64_t shard = inbox_index / SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD; + uint64_t shard_begin = shard * SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD; + uint64_t shard_end = shard_begin + SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD; + if (shard_end > scheduler_count) shard_end = scheduler_count; uint64_t valid_bits = shard_end > shard_begin ? (UINT64_C(1) << (shard_end - shard_begin)) - 1 : 0; return scheduler_gm_query(directory->core_types[core_type_index][shard].bits) & valid_bits; } @@ -830,12 +829,12 @@ inline __aicore__ bool scheduler_steal_ready_from_shard( inline __aicore__ bool scheduler_claim_ready_for_slot( const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *context, - __gm__ SchedulerRunControl *run_control, uint64_t resolver_count, uint32_t core_type_index, uint64_t *victim_cursor, - SchedulerReadyStats *stats, SchedulerReadyClaim *claim, __gm__ SchedulerReadyOwnerState *owner_state, - bool trace_enabled = false + __gm__ SchedulerRunControl *run_control, uint64_t scheduler_count, uint32_t core_type_index, + uint64_t *victim_cursor, SchedulerReadyStats *stats, SchedulerReadyClaim *claim, + __gm__ SchedulerReadyOwnerState *owner_state, bool trace_enabled = false ) { - if (victim_cursor == nullptr || claim == nullptr || owner_state == nullptr || resolver_count == 0 || - resolver_count > SCHEDULER_RESOLVER_CAPACITY || context->inbox_index >= resolver_count || + if (victim_cursor == nullptr || claim == nullptr || owner_state == nullptr || scheduler_count == 0 || + scheduler_count > SCHEDULER_CAPACITY || context->inbox_index >= scheduler_count || core_type_index >= SCHEDULER_CORE_TYPE_COUNT) return false; *claim = {}; @@ -854,14 +853,14 @@ inline __aicore__ bool scheduler_claim_ready_for_slot( } __gm__ SchedulerReadyDirectory *directory = scheduler_ready_directory_at(scheduler_state_base, context); - uint64_t shard_begin = context->inbox_index / SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD * - SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD; - uint64_t shard_end = shard_begin + SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD; - if (shard_end > resolver_count) shard_end = resolver_count; + uint64_t shard_begin = + context->inbox_index / SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD * SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD; + uint64_t shard_end = shard_begin + SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD; + if (shard_end > scheduler_count) shard_end = scheduler_count; uint64_t start = *victim_cursor; if (start < shard_begin || start >= shard_end) start = shard_begin; uint64_t bits = - scheduler_load_ready_directory_shard(directory, resolver_count, core_type_index, context->inbox_index); + scheduler_load_ready_directory_shard(directory, scheduler_count, core_type_index, context->inbox_index); if (bits != 0 && !scheduler_steal_ready_from_shard( graph, scheduler_state_base, context, run_control, core_type_index, shard_begin, shard_end, start, bits, stats, claim, trace_enabled @@ -875,14 +874,14 @@ inline __aicore__ bool scheduler_claim_ready_for_slot( } inline __aicore__ bool scheduler_ready_directory_nonempty( - __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *context, uint64_t resolver_count, + __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *context, uint64_t scheduler_count, uint32_t core_type_index ) { - if (resolver_count == 0 || resolver_count > SCHEDULER_RESOLVER_CAPACITY || context->inbox_index >= resolver_count || + if (scheduler_count == 0 || scheduler_count > SCHEDULER_CAPACITY || context->inbox_index >= scheduler_count || core_type_index >= SCHEDULER_CORE_TYPE_COUNT) return false; __gm__ SchedulerReadyDirectory *directory = scheduler_ready_directory_at(scheduler_state_base, context); - return scheduler_load_ready_directory_shard(directory, resolver_count, core_type_index, context->inbox_index) != 0; + return scheduler_load_ready_directory_shard(directory, scheduler_count, core_type_index, context->inbox_index) != 0; } inline __aicore__ void scheduler_initialize_free_slot(__gm__ SchedulerDispatchSlot *slot) { @@ -897,17 +896,18 @@ inline __aicore__ void scheduler_initialize_free_slot(__gm__ SchedulerDispatchSl } inline __aicore__ bool scheduler_fill_dispatch_slot( - const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *resolver, + const SchedulerGraphView &graph, __gm__ void *scheduler_state_base, __gm__ SchedulerWorkerContext *scheduler, __gm__ SchedulerRunControl *run_control, const SchedulerFreeSlotClaim &slot_claim, const SchedulerReadyClaim &ready_claim, bool trace_enabled = false, SchedulerDispatchFillTiming *timing = nullptr ) { if (ready_claim.task_id < 0 || static_cast(ready_claim.task_id) >= graph.task_count || - slot_claim.worker_id >= resolver->runtime_worker_count || slot_claim.slot_index >= SCHEDULER_PENDING_SLOT_COUNT) + slot_claim.worker_id >= scheduler->runtime_worker_count || + slot_claim.slot_index >= SCHEDULER_PENDING_SLOT_COUNT) return false; const bool record_timeline = timing != nullptr; uint64_t operation_start = record_timeline ? scheduler_cycles() : 0; __gm__ SchedulerTaskMetadata *metadata_source = - scheduler_task_metadata_at(scheduler_state_base, resolver, ready_claim.task_id); + scheduler_task_metadata_at(scheduler_state_base, scheduler, ready_claim.task_id); scheduler_observe_cache_line(metadata_source); SchedulerTaskMetadata metadata{}; metadata.kernel_ids[0] = metadata_source->kernel_ids[0]; @@ -920,7 +920,7 @@ inline __aicore__ bool scheduler_fill_dispatch_slot( metadata.timing_slot = metadata_source->timing_slot; const uint8_t subtask_slot = scheduler_metadata_single_subtask_slot(metadata.active_mask); __gm__ SchedulerWorkerContext *target = - scheduler_worker_context_at(scheduler_state_base, resolver, slot_claim.worker_id); + scheduler_worker_context_at(scheduler_state_base, scheduler, slot_claim.worker_id); scheduler_observe_cache_line(target); if (subtask_slot == UINT8_MAX || (target->core_type != static_cast(CoreType::AIC) && @@ -928,23 +928,23 @@ inline __aicore__ bool scheduler_fill_dispatch_slot( !scheduler_task_is_executable(metadata.flags) || scheduler_task_is_gang(metadata.flags) || scheduler_metadata_core_type_index(subtask_slot) != scheduler_core_type_index(target->core_type)) { scheduler_record_error( - run_control, ready_claim.task_id, SchedulerGraphResult::UNSUPPORTED_SHAPE, &graph, resolver, + run_control, ready_claim.task_id, SchedulerGraphResult::UNSUPPORTED_SHAPE, &graph, scheduler, SchedulerErrorSite::DISPATCH_INVALID_SHAPE ); return false; } const uint16_t kernel_id = metadata.kernel_ids[subtask_slot]; __gm__ SchedulerDispatchSlot *slot = - scheduler_dispatch_slot_at(scheduler_state_base, resolver, slot_claim.worker_id, slot_claim.slot_index); + scheduler_dispatch_slot_at(scheduler_state_base, scheduler, slot_claim.worker_id, slot_claim.slot_index); uint32_t generation = slot_claim.generation + 1; if (generation == 0) generation = 1; __gm__ uint64_t *callable_addresses = - scheduler_state_at(scheduler_state_base, resolver->callable_addresses_offset); + scheduler_state_at(scheduler_state_base, scheduler->callable_addresses_offset); const bool inline_task = scheduler_task_is_inline(metadata.flags); uint64_t callable_address = UINT64_C(1); if (!inline_task && !scheduler_lookup_callable_address(callable_addresses, kernel_id, &callable_address)) { scheduler_record_error( - run_control, ready_claim.task_id, SchedulerGraphResult::INVALID_CALLABLE, &graph, resolver, + run_control, ready_claim.task_id, SchedulerGraphResult::INVALID_CALLABLE, &graph, scheduler, SchedulerErrorSite::DISPATCH_INVALID_CALLABLE ); return false; @@ -954,7 +954,7 @@ inline __aicore__ bool scheduler_fill_dispatch_slot( slot->ready_inbox_index = ready_claim.inbox_index; slot->claim_start_cycles = ready_claim.claim_start_cycles; slot->claim_end_cycles = ready_claim.claim_end_cycles; - slot->claim_worker_id = resolver->worker_index; + slot->claim_worker_id = scheduler->worker_index; slot->kernel_id = kernel_id; slot->subtask_slot = subtask_slot; slot->has_fanin = scheduler_task_has_fanin(metadata.flags) ? 1 : 0; @@ -992,7 +992,7 @@ inline __aicore__ bool scheduler_fill_dispatch_slot( const SchedulerPredicateResult predicate = scheduler_evaluate_task_predicate(graph, ready_claim.task_id); if (predicate == SchedulerPredicateResult::MALFORMED) { scheduler_record_error( - run_control, ready_claim.task_id, SchedulerGraphResult::INVALID_ARGUMENTS, &graph, resolver, + run_control, ready_claim.task_id, SchedulerGraphResult::INVALID_ARGUMENTS, &graph, scheduler, SchedulerErrorSite::DISPATCH_INVALID_PREDICATE ); return false; @@ -1002,7 +1002,7 @@ inline __aicore__ bool scheduler_fill_dispatch_slot( } if (status != SchedulerGraphResult::OK) { scheduler_record_error( - run_control, ready_claim.task_id, status, &graph, resolver, SchedulerErrorSite::DISPATCH_MATERIALIZE_FAILED + run_control, ready_claim.task_id, status, &graph, scheduler, SchedulerErrorSite::DISPATCH_MATERIALIZE_FAILED ); return false; } @@ -1010,7 +1010,7 @@ inline __aicore__ bool scheduler_fill_dispatch_slot( if (timing != nullptr) timing->materialize_cycles += materialize_end - operation_end; scheduler_publish_dispatch_payload(payload); __gm__ SchedulerTaskControl *control = - scheduler_task_control_at(scheduler_state_base, resolver, ready_claim.task_id); + scheduler_task_control_at(scheduler_state_base, scheduler, ready_claim.task_id); if (trace_enabled) { scheduler_observe_cache_line(&control->next_waiter); control->ready_publish_cycles = scheduler_cycles(); @@ -1044,7 +1044,7 @@ inline __aicore__ bool scheduler_resolve_completion( if (trace_enabled) { scheduler_observe_cache_line(&control->next_waiter); control->completion_resolve_start_cycles = resolve_start; - control->resolver_worker_id = context->worker_index; + control->scheduler_worker_id = context->worker_index; } int64_t waiter = scheduler_gm_exchange(control->wake_list_head, SCHEDULER_WAKE_LIST_CLOSED); if (waiter == SCHEDULER_WAKE_LIST_CLOSED) { diff --git a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_topology.h b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_topology.h index da6c590200..5527c3444b 100644 --- a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_topology.h +++ b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_topology.h @@ -22,7 +22,7 @@ struct SchedulerClusterCoordinate { // AIC indices occupy [0, cluster_count), followed by the two AIV subblocks of // every Cluster. Real A5 physical_core_id values are sparse and are only valid // for register addressing, so they must not be treated as a dense topology. -// Resolver selection remains dynamic and is performed after discovery. +// Scheduler selection remains dynamic and is performed after discovery. inline bool scheduler_cluster_coordinate_from_worker( int32_t worker_id, bool is_aic, int32_t cluster_count, int32_t aiv_per_cluster, SchedulerClusterCoordinate *coordinate diff --git a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_types.h b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_types.h index a50bdf506c..eb78eb7f6b 100644 --- a/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_types.h +++ b/src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_types.h @@ -625,12 +625,11 @@ inline constexpr uint32_t SCHEDULER_PENDING_SLOT_COUNT = 2; inline constexpr uint32_t SCHEDULER_CALLABLE_CAPACITY = 1024; inline constexpr uint32_t SCHEDULER_CORE_TYPE_COUNT = 2; inline constexpr uint32_t SCHEDULER_CLUSTER_CAPACITY = SCHEDULER_WORKER_CAPACITY / 3; -inline constexpr uint32_t SCHEDULER_RESOLVER_CAPACITY = SCHEDULER_CLUSTER_CAPACITY; +inline constexpr uint32_t SCHEDULER_CAPACITY = SCHEDULER_CLUSTER_CAPACITY; inline constexpr uint32_t SCHEDULER_GANG_COHORT_COUNT = 2; -inline constexpr uint32_t SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD = 7; +inline constexpr uint32_t SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD = 7; inline constexpr uint32_t SCHEDULER_READY_DIRECTORY_SHARD_COUNT = - (SCHEDULER_RESOLVER_CAPACITY + SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD - 1) / - SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD; + (SCHEDULER_CAPACITY + SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD - 1) / SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD; inline constexpr int64_t SCHEDULER_TASK_ID_INVALID = -1; inline constexpr int64_t SCHEDULER_WAKE_LIST_OPEN = -1; inline constexpr int64_t SCHEDULER_WAKE_LIST_CLOSED = -2; @@ -740,7 +739,7 @@ struct alignas(128) SchedulerTaskControl { uint64_t completion_resolve_start_cycles; uint64_t completion_resolve_end_cycles; uint64_t ready_publish_cycles; - uint64_t resolver_worker_id; + uint64_t scheduler_worker_id; uint8_t scheduler_line_padding[16]; }; @@ -784,7 +783,7 @@ struct alignas(128) SchedulerGangCoordinator { uint64_t next_generation; uint64_t scan_cursor; uint64_t gang_task_count; - uint64_t resolver_count; + uint64_t scheduler_count; uint64_t cohort_count; uint64_t reserved0; uint64_t owner_reserved; @@ -814,8 +813,8 @@ struct alignas(128) SchedulerGangCohort { uint64_t reserved[3]; }; -// One Resolver owns one participant cell. The second line contains generation -// tokens observed only by its parent in the binary Resolver tree. +// One Scheduler owns one participant cell. The second line contains generation +// tokens observed only by its parent in the binary Scheduler tree. struct alignas(128) SchedulerGangParticipant { volatile uint64_t config_generation; int64_t task_id; @@ -840,7 +839,7 @@ struct alignas(128) SchedulerGangParticipant { volatile uint64_t completion_subtree_token; }; -// Each Resolver polls its own command line. Resolver0 seeds the root and every +// Each Scheduler polls its own command line. Scheduler0 seeds the root and every // parent forwards transitions to two children, avoiding a globally contended // cohort line and bounding sync-start release skew by the tree depth. struct alignas(128) SchedulerGangCommand { @@ -859,7 +858,7 @@ struct alignas(128) SchedulerReadyDirectory { volatile uint64_t bootstrap_ready_types[SCHEDULER_WORKER_CAPACITY]; }; -// Resolver-owned metadata occupies the first line. The Executor polls only +// Scheduler-owned metadata occupies the first line. The Executor polls only // publication in the second line. struct alignas(128) SchedulerDispatchSlot { int64_t task_id; @@ -940,7 +939,7 @@ struct alignas(128) SchedulerRunControl { uint64_t aiv_worker_demand; uint64_t gang_coordinator_offset; uint64_t gang_cohorts_offset; - uint64_t resolver_count; + uint64_t scheduler_count; volatile uint64_t executed_task_count; volatile uint64_t resolved_task_count; @@ -1055,12 +1054,12 @@ struct alignas(128) SchedulerWorkerContext { volatile uint64_t gang_cohorts_offset; volatile uint64_t gang_participants_offset; volatile uint64_t gang_commands_offset; - volatile uint64_t resolver_count; + volatile uint64_t scheduler_count; volatile uint64_t cluster_count; volatile uint64_t cluster_index; - volatile uint64_t resolver_index; - volatile uint64_t resolver_worker_id; - volatile uint64_t is_resolver; + volatile uint64_t scheduler_index; + volatile uint64_t scheduler_worker_id; + volatile uint64_t is_scheduler; volatile uint64_t cluster_worker_ids[3]; uint64_t topology_reserved[3]; @@ -1139,7 +1138,7 @@ struct alignas(128) SchedulerTaskTrace { uint64_t register_release_cycles; uint64_t descriptor_cache_observed_cycles; uint64_t completion_prepare_start_cycles; - uint64_t refill_resolver_worker_id; + uint64_t refill_scheduler_worker_id; uint64_t refill_start_cycles; uint64_t refill_end_cycles; uint64_t refill_task_id; @@ -1190,11 +1189,10 @@ static_assert(sizeof(SchedulerGangCommand) == 128, "gang command layout changed" static_assert(alignof(SchedulerGangCommand) == 128, "gang command alignment changed"); static_assert(sizeof(SchedulerReadyDirectoryShard) == 64, "ready directory shard must occupy one cache line"); static_assert(alignof(SchedulerReadyDirectoryShard) == 64, "ready directory shard alignment changed"); -static_assert(SCHEDULER_RESOLVER_CAPACITY <= SCHEDULER_WORKER_CAPACITY, "resolver capacity exceeds worker storage"); +static_assert(SCHEDULER_CAPACITY <= SCHEDULER_WORKER_CAPACITY, "scheduler capacity exceeds worker storage"); static_assert( - SCHEDULER_READY_DIRECTORY_SHARD_COUNT * SCHEDULER_READY_DIRECTORY_RESOLVERS_PER_SHARD >= - SCHEDULER_RESOLVER_CAPACITY, - "ready directory does not cover every resolver" + SCHEDULER_READY_DIRECTORY_SHARD_COUNT * SCHEDULER_READY_DIRECTORY_OWNERS_PER_SHARD >= SCHEDULER_CAPACITY, + "ready directory does not cover every scheduler" ); static_assert( offsetof(SchedulerReadyDirectory, bootstrap_ready_types) == @@ -1328,7 +1326,7 @@ inline bool scheduler_plan_layout( !SCHEDULER_RESERVE_ARRAY( SCHEDULER_CORE_TYPE_COUNT * SCHEDULER_WORKER_CAPACITY, SchedulerReadyInbox, ready_inboxes_offset ) || - !SCHEDULER_RESERVE_ARRAY(SCHEDULER_RESOLVER_CAPACITY, SchedulerReadyOwnerState, ready_owner_states_offset) || + !SCHEDULER_RESERVE_ARRAY(SCHEDULER_CAPACITY, SchedulerReadyOwnerState, ready_owner_states_offset) || !scheduler_layout_reserve( &cursor, sizeof(SchedulerReadyDirectory), alignof(SchedulerReadyDirectory), &next.ready_directory_offset ) || @@ -1369,7 +1367,7 @@ inline bool scheduler_init_data_from_layout(void *base, const AicoreSchedulerLay for (uint64_t i = 0; i < SCHEDULER_CORE_TYPE_COUNT * SCHEDULER_WORKER_CAPACITY; ++i) ready[i].head = SCHEDULER_INBOX_EMPTY; auto *ready_owners = scheduler_state_at(base, layout.ready_owner_states_offset); - for (uint64_t owner = 0; owner < SCHEDULER_RESOLVER_CAPACITY; ++owner) { + for (uint64_t owner = 0; owner < SCHEDULER_CAPACITY; ++owner) { for (uint32_t type = 0; type < SCHEDULER_CORE_TYPE_COUNT; ++type) ready_owners[owner].queues[type].pending_endpoints = SCHEDULER_READY_PENDING_EMPTY; } @@ -1377,8 +1375,8 @@ inline bool scheduler_init_data_from_layout(void *base, const AicoreSchedulerLay for (uint64_t worker = 0; worker < SCHEDULER_WORKER_CAPACITY; ++worker) { contexts[worker].physical_core_id = -1; contexts[worker].cluster_index = UINT64_MAX; - contexts[worker].resolver_index = UINT64_MAX; - contexts[worker].resolver_worker_id = UINT64_MAX; + contexts[worker].scheduler_index = UINT64_MAX; + contexts[worker].scheduler_worker_id = UINT64_MAX; contexts[worker].cluster_worker_ids[0] = UINT64_MAX; contexts[worker].cluster_worker_ids[1] = UINT64_MAX; contexts[worker].cluster_worker_ids[2] = UINT64_MAX; diff --git a/src/a5/runtime/tensormap_and_ringbuffer/docs/profiling_levels.md b/src/a5/runtime/tensormap_and_ringbuffer/docs/profiling_levels.md index 4db296a856..811a1ce244 100644 --- a/src/a5/runtime/tensormap_and_ringbuffer/docs/profiling_levels.md +++ b/src/a5/runtime/tensormap_and_ringbuffer/docs/profiling_levels.md @@ -226,7 +226,7 @@ mirrors the PMU pattern — two independent channels (one binary, one int): (shared memory). Host writes it in `ChipSwimlaneCollector::initialize`; AICPU promotes it from the header in `chip_swimlane_aicpu_init` and exposes it via `get_chip_swimlane_level()` (typed `ChipSwimlaneLevel`) for - `>= AICPU_TIMING / SCHED_PHASES / ORCH_PHASES` gates. + `>= SCHEDULE_TIMING / SCHED_PHASES / ORCH_PHASES` gates. On sim, the binary on/off travels via the dlsym'd `set_chip_swimlane_enabled` entry point; the granular level still goes through the shared-memory @@ -236,7 +236,7 @@ header just like on onboard. | ----- | -------- | | 0 | Nothing (disabled) | | 1 | AICore timing only (start/end/task_token_raw) — AICPU `complete_task` is bypassed | -| 2 | + dispatch_time, finish_time | +| 2 | + Scheduler per-task dispatch_time, finish_time | | 3 | + Scheduler phases (`SCHED_*`) | | 4 | + Orchestrator phases (full) | @@ -286,10 +286,10 @@ content it depends on instead of relying on magic numbers: // Cheap binary check, available immediately after kernel entry. if (is_chip_swimlane_enabled()) { ... } -// AICPU dispatch/finish timestamps. +// Scheduler per-task dispatch/finish timestamps (AICPU-produced in this runtime). // Granular checks below require chip_swimlane_aicpu_init to have already run // (so the level has been promoted from the shared-memory header). -if (get_chip_swimlane_level() >= ChipSwimlaneLevel::AICPU_TIMING) { ... } +if (get_chip_swimlane_level() >= ChipSwimlaneLevel::SCHEDULE_TIMING) { ... } // Scheduler main-loop phase records (SCHED_*) if (get_chip_swimlane_level() >= ChipSwimlaneLevel::SCHED_PHASES) { ... } @@ -305,8 +305,8 @@ shared-memory field and mirrors `PmuEventType : uint32_t`): | Enumerator | Underlying value | | ---------- | ---------------- | | `DISABLED` | 0 | -| `AICORE_TIMING` | 1 | -| `AICPU_TIMING` | 2 | +| `TASK_TIMING` | 1 | +| `SCHEDULE_TIMING` | 2 | | `SCHED_PHASES` | 3 | | `ORCH_PHASES` | 4 | diff --git a/src/a5/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_completion.cpp b/src/a5/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_completion.cpp index 24f85c59c4..0b00fdcd19 100644 --- a/src/a5/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_completion.cpp +++ b/src/a5/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_completion.cpp @@ -215,12 +215,12 @@ void SchedulerContext::complete_slot_task( } #if SIMPLER_DFX - // Level gate: at AICORE_TIMING (level=1) the AICore record alone carries + // Level gate: at TASK_TIMING (level=1) the AICore record alone carries // {start, end, task_token_raw}, host resolves func_id/core_type from // dep_gen / per-core mapping, and AICPU has nothing to write. Only at - // AICPU_TIMING (level=2) and above does AICPU contribute dispatch/finish + // SCHEDULE_TIMING (level=2) and above does AICPU contribute dispatch/finish // timestamps via complete_task. - if (chip_swimlane.chip_swimlane_enabled && chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane.chip_swimlane_enabled && chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { #if SIMPLER_SCHED_PROFILING uint64_t t_perf_start = get_sys_cnt_aicpu(); #endif @@ -343,7 +343,7 @@ void SchedulerContext::check_running_cores_for_completion( // BEFORE any fanin / deferred-release work. Anything later would // charge AICPU completion-processing cost to (end → finish). uint64_t finish_ts = 0; - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING && (t.pending_done || t.running_done)) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING && (t.pending_done || t.running_done)) { finish_ts = get_sys_cnt_aicpu(); } #endif @@ -529,7 +529,7 @@ SchedulerContext::SyncStartStageResult SchedulerContext::stage_sync_start_cores( sched_chip_swimlane_[thread_idx].sched_loop_count, static_cast(handle_count) ); } - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { dispatch_ts = pub_t0 != 0 ? pub_t0 : get_sys_cnt_aicpu(); } #endif diff --git a/src/a5/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_context.h b/src/a5/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_context.h index a6c7106edd..e4115806eb 100644 --- a/src/a5/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_context.h +++ b/src/a5/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_context.h @@ -262,7 +262,7 @@ class SchedulerContext { // // dispatch_timestamp_slot points to the CoreExecState slot // (pending_dispatch_timestamp / running_dispatch_timestamp) selected at - // prepare time, or nullptr when chip swimlane is below AICPU_TIMING and no + // prepare time, or nullptr when chip swimlane is below SCHEDULE_TIMING and no // dispatch timestamp is being recorded. struct PublishHandle { uint64_t reg_addr; diff --git a/src/a5/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_dispatch.cpp b/src/a5/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_dispatch.cpp index 381f863b7c..9d3474cbf9 100644 --- a/src/a5/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_dispatch.cpp +++ b/src/a5/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_dispatch.cpp @@ -194,7 +194,7 @@ SchedulerContext::PublishHandle SchedulerContext::prepare_subtask_to_core( uint64_t *dispatch_timestamp_slot = nullptr; #if SIMPLER_DFX - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { dispatch_timestamp_slot = to_pending ? &core_exec_state.pending_dispatch_timestamp : &core_exec_state.running_dispatch_timestamp; } @@ -344,7 +344,7 @@ void SchedulerContext::dispatch_shape( wmb(); uint64_t dispatch_ts = 0; #if SIMPLER_DFX - if (chip_swimlane_level_ >= ChipSwimlaneLevel::AICPU_TIMING) { + if (chip_swimlane_level_ >= ChipSwimlaneLevel::SCHEDULE_TIMING) { dispatch_ts = get_sys_cnt_aicpu(); } #endif diff --git a/src/common/host_build_graph/docs/GRAPH_EXECUTION.md b/src/common/host_build_graph/docs/GRAPH_EXECUTION.md index cbea09d5ff..ce99cd654f 100644 --- a/src/common/host_build_graph/docs/GRAPH_EXECUTION.md +++ b/src/common/host_build_graph/docs/GRAPH_EXECUTION.md @@ -399,7 +399,7 @@ In-graph task storage remains untouched until bounded materialization begins. Host orchestration builds the complete task image before device execution. At the end of orchestration, the Host copies one bind image containing the compacted shared-memory task window and argument pools, then launches the -resident Scheduler. Slot task and payload references remain self-relative; +Scheduler. Slot task and payload references remain self-relative; `graph_context` is the absolute address of the retained Definition object until initial classification localizes the execution in the outer heap. diff --git a/src/common/platform/include/aicpu/chip_swimlane_collector_aicpu.h b/src/common/platform/include/aicpu/chip_swimlane_collector_aicpu.h index 7e7ba4a43b..60de2e6415 100644 --- a/src/common/platform/include/aicpu/chip_swimlane_collector_aicpu.h +++ b/src/common/platform/include/aicpu/chip_swimlane_collector_aicpu.h @@ -38,7 +38,7 @@ * - granular ChipSwimlaneLevel — `ChipSwimlaneDataHeader::chip_swimlane_level` * (shared memory); read in `chip_swimlane_aicpu_init` and cached, then queried * via `get_chip_swimlane_level()` for - * `>= AICPU_TIMING / SCHED_PHASES / ORCH_PHASES` gates. + * `>= SCHEDULE_TIMING / SCHED_PHASES / ORCH_PHASES` gates. */ extern "C" void set_platform_chip_swimlane_base(uint64_t chip_swimlane_data_base); extern "C" uint64_t get_platform_chip_swimlane_base(); @@ -89,7 +89,7 @@ void chip_swimlane_aicpu_init(int worker_count); * ACK-gated release (see chip_swimlane_aicpu_on_aicore_ack). * 3. Bump the AICore pool's `total_record_count` so host reconcile * (total == collected + dropped) stays accurate at all levels — - * including AICORE_TIMING (level=1), where `complete_task` is bypassed. + * including TASK_TIMING (level=1), where `complete_task` is bypassed. * * Race safety: rotation runs BEFORE the dispatch register write. The * completion-before-dispatch invariant proves prior tasks FIN'd, but diff --git a/src/common/platform/include/common/chip_swimlane_profiling.h b/src/common/platform/include/common/chip_swimlane_profiling.h index 5cbd5f5a07..38707d0801 100644 --- a/src/common/platform/include/common/chip_swimlane_profiling.h +++ b/src/common/platform/include/common/chip_swimlane_profiling.h @@ -78,11 +78,11 @@ // enum is the canonical in-code type used for comparisons. // ============================================================================= enum class ChipSwimlaneLevel : uint32_t { - DISABLED = 0, // No collection at all - AICORE_TIMING = 1, // AICore per-task start/end timestamps + task record buffer - AICPU_TIMING = 2, // + AICPU dispatch/finish timestamps - SCHED_PHASES = 3, // + scheduler main-loop phase records (SCHED_COMPLETE/DISPATCH/IDLE_WAIT) - ORCH_PHASES = 4, // + orchestrator phase records + DISABLED = 0, // No collection at all + TASK_TIMING = 1, // AICore per-task start/end timestamps + task record buffer + SCHEDULE_TIMING = 2, // + Scheduler per-task dispatch/finish timestamps + SCHED_PHASES = 3, // + scheduler main-loop phase records (SCHED_COMPLETE/DISPATCH/IDLE_WAIT) + ORCH_PHASES = 4, // + orchestrator phase records }; // ============================================================================= @@ -90,11 +90,12 @@ enum class ChipSwimlaneLevel : uint32_t { // ============================================================================= /** - * AICPU-side timing record. The minimal AICPU-only payload after the + * AICPU Scheduler timing record. The minimal AICPU-only payload after the * AICore-as-producer split: identity (task_token_raw, core_type) and * AICore-side timing (start/end) all live in ChipSwimlaneAicoreTaskRecord; the - * AICPU record only carries the two timestamps the AICore side cannot - * produce, plus the host-side join key against the AICore stream. + * AICPU record carries only the two timestamps the AICore side cannot produce + * (the scheduler's dispatch/finish), plus the host-side join key against the + * AICore stream. * * - dispatch_time : AICPU timestamp when DATA_MAIN_BASE was written. * - finish_time : AICPU timestamp when AICPU observed FIN. @@ -105,7 +106,7 @@ enum class ChipSwimlaneLevel : uint32_t { * the matched AICore record, derives core_type from the per-core static * table published via ChipSwimlaneCollector::set_core_types, and emits * func_id = -1 (resolved post-process by `swimlane_converter.py` from - * deps.json's `kernel_ids[]`). Same path AICORE_TIMING (level=1) uses. + * deps.json's `kernel_ids[]`). Same path TASK_TIMING (level=1) uses. * * Fanout edges live in the static DAG (deps.json from dep_gen) — not in * this record. Keeping fanout out of the hot AICPU commit path avoids a @@ -133,8 +134,9 @@ static_assert(sizeof(ChipSwimlaneAicpuTaskRecord) == 32, "ChipSwimlaneAicpuTaskR /** * Slim per-task record written by AICore directly into its own per-core * output buffer (no staging slot, no AICPU read). AICPU never touches this - * record at AICORE_TIMING (level=1); at AICPU_TIMING+ the host joins it - * against the AICPU record stream on `reg_task_id` (NOT `task_token_raw`). + * record at TASK_TIMING (level=1); at SCHEDULE_TIMING+ the host joins it + * against the active Scheduler producer's record stream on `reg_task_id` + * (NOT `task_token_raw`). * * Two identity fields with different roles: * @@ -420,7 +422,7 @@ struct ChipSwimlaneDataHeader { // Metadata (Host initializes, Device read-only) uint32_t num_cores; // Actual number of cores launched - uint32_t chip_swimlane_level; // 0=off, 1=AICore timing, 2=+dispatch/fanout, + uint32_t chip_swimlane_level; // 0=off, 1=AICore timing, 2=+Scheduler task timing, // 3=+sched phases, 4=+orch phases. Host writes // at init; AICPU reads in chip_swimlane_aicpu_init. diff --git a/src/common/platform/include/host/chip_swimlane_collector.h b/src/common/platform/include/host/chip_swimlane_collector.h index 99164e123b..3c5076f0ff 100644 --- a/src/common/platform/include/host/chip_swimlane_collector.h +++ b/src/common/platform/include/host/chip_swimlane_collector.h @@ -359,8 +359,8 @@ class ChipSwimlaneCollector : public profiling_common::ProfilerBase= AICPU_TIMING / SCHED_PHASES / ORCH_PHASES` gates have the granular +// `>= SCHEDULE_TIMING / SCHED_PHASES / ORCH_PHASES` gates have the granular // value (exposed via get_chip_swimlane_level()). static uint64_t g_platform_chip_swimlane_base = 0; static bool g_enable_chip_swimlane = false; @@ -549,7 +549,7 @@ static void aicore_rotate(int core_id, int thread_idx, uint32_t new_buf_first_re // // total_record_count accounting also lives here: one AICore record == one // dispatch, so the dispatch count IS the AICore-side total. Bumping here -// (instead of inside complete_task) means level=1 (AICORE_TIMING-only) gets +// (instead of inside complete_task) means level=1 (TASK_TIMING-only) gets // accurate reconcile counts even when complete_task is bypassed. void chip_swimlane_aicpu_on_aicore_dispatch(int core_id, int thread_idx, uint32_t reg_task_id) { if (!g_enable_chip_swimlane) { @@ -724,15 +724,15 @@ void chip_swimlane_aicpu_flush(int thread_idx, const int *cur_thread_cores, int uint64_t ac_buf_ptr = ac_state->head.current_buf_ptr; if (ac_buf_ptr == 0) continue; - // At AICPU_TIMING+, `total_record_count` is bumped on every complete + // At SCHEDULE_TIMING+, `total_record_count` is bumped on every complete // and gives an accurate live count for the current buffer. At - // AICORE_TIMING (level=1) complete_task is skipped, so that counter + // TASK_TIMING (level=1) complete_task is skipped, so that counter // stays 0 and the formula bails even when AICore has filled records. // Fall back to the buffer's full capacity in that case; the host-side // copy_aicore_buffer skips trailing slots whose start_time is still 0, // so over-stating count costs only a scan pass — never spurious records. uint32_t ac_mark; - if (g_chip_swimlane_level >= ChipSwimlaneLevel::AICPU_TIMING) { + if (g_chip_swimlane_level >= ChipSwimlaneLevel::SCHEDULE_TIMING) { uint32_t live = ac_state->head.total_record_count - ac_state->head.current_buf_seq * static_cast(PLATFORM_AICORE_BUFFER_SIZE); if (live == 0) { diff --git a/src/common/platform/shared/host/chip_swimlane_collector.cpp b/src/common/platform/shared/host/chip_swimlane_collector.cpp index 1f52a6e1ea..859c82461f 100644 --- a/src/common/platform/shared/host/chip_swimlane_collector.cpp +++ b/src/common/platform/shared/host/chip_swimlane_collector.cpp @@ -208,7 +208,7 @@ int ChipSwimlaneCollector::initialize( // Phase metadata: must be zero-initialized here. alloc_cb returns // uninitialized device memory; AICPU only writes these fields when // phase init runs (level >= SCHED_PHASES). Without zeroing, lower - // levels (AICORE_TIMING / AICPU_TIMING) leave garbage that + // levels (TASK_TIMING / SCHEDULE_TIMING) leave garbage that // for_each_instance iterates as `num_sched_phase_threads` / // `num_orch_phase_threads`, walking off the end of the allocated pool // array → segfault. The host-side reader (read_phase_header_metadata) @@ -931,7 +931,7 @@ void ChipSwimlaneCollector::record_clock_anchor_samples(std::vector(scheduler_state->base(), layout.ready_owner_states_offset); run_control->aiv_active_worker_count = workers; - run_control->resolver_count = workers; + run_control->scheduler_count = workers; for (uint64_t worker = 0; worker < workers; ++worker) { SchedulerWorkerContext &context = contexts[worker]; context.core_type = static_cast(CoreType::AIV); @@ -160,22 +160,22 @@ struct FixtureStorage { void configure_normal_aiv_cluster(FixtureStorage &storage, uint64_t task_count) { storage.contexts[0].core_type = static_cast(CoreType::AIC); - SchedulerWorkerContext &resolver = storage.contexts[1]; - resolver.is_resolver = 1; - resolver.resolver_index = 0; - resolver.resolver_count = 1; - resolver.inbox_index = 0; - resolver.cluster_worker_ids[0] = 0; - resolver.cluster_worker_ids[1] = 1; - resolver.cluster_worker_ids[2] = 2; - storage.run_control->resolver_count = 1; + SchedulerWorkerContext &scheduler = storage.contexts[1]; + scheduler.is_scheduler = 1; + scheduler.scheduler_index = 0; + scheduler.scheduler_count = 1; + scheduler.inbox_index = 0; + scheduler.cluster_worker_ids[0] = 0; + scheduler.cluster_worker_ids[1] = 1; + scheduler.cluster_worker_ids[2] = 2; + storage.run_control->scheduler_count = 1; auto *callables = scheduler_state_at(storage.scheduler_state->base(), storage.layout.callable_addresses_offset); callables[1] = 0x1000; for (uint64_t worker = 0; worker < 3; ++worker) { for (uint32_t slot = 0; slot < SCHEDULER_PENDING_SLOT_COUNT; ++slot) scheduler_initialize_free_slot( - scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, worker, slot) + scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, worker, slot) ); } for (uint64_t task = 0; task < task_count; ++task) { @@ -187,29 +187,29 @@ void configure_normal_aiv_cluster(FixtureStorage &storage, uint64_t task_count) } void enqueue_normal_aiv_tasks( - FixtureStorage &storage, SchedulerWorkerContext &resolver, uint64_t task_begin, uint64_t task_end + FixtureStorage &storage, SchedulerWorkerContext &scheduler, uint64_t task_begin, uint64_t task_end ) { SchedulerReadyBatch batch{}; SchedulerReadyStats ready_stats{}; for (uint64_t task = task_begin; task < task_end; ++task) { auto *control = - scheduler_task_control_at(storage.scheduler_state->base(), &resolver, static_cast(task)); + scheduler_task_control_at(storage.scheduler_state->base(), &scheduler, static_cast(task)); control->state = static_cast(SchedulerTaskState::READY); ASSERT_TRUE(scheduler_ready_batch_append( - storage.scheduler_state->base(), &resolver, static_cast(task), &batch, &ready_stats + storage.scheduler_state->base(), &scheduler, static_cast(task), &batch, &ready_stats )); } ASSERT_TRUE(scheduler_ready_batch_push( - storage.scheduler_state->base(), &resolver, 1, 0, &batch, &ready_stats, - &storage.owner_states[resolver.inbox_index] + storage.scheduler_state->base(), &scheduler, 1, 0, &batch, &ready_stats, + &storage.owner_states[scheduler.inbox_index] )); } void occupy_normal_slot( - FixtureStorage &storage, SchedulerWorkerContext &resolver, uint64_t worker_id, uint32_t pending_slot, + FixtureStorage &storage, SchedulerWorkerContext &scheduler, uint64_t worker_id, uint32_t pending_slot, int64_t task_id ) { - auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, worker_id, pending_slot); + auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, worker_id, pending_slot); slot->task_id = task_id; slot->subtask_slot = 1; slot->gang = 0; @@ -231,30 +231,30 @@ TEST(SchedulerClusterCompletion, SpscGenerationCompletesNormalTask) { FixtureStorage storage(1, 3); GraphBuffer graph(1); graph.executable(0, 0); - SchedulerWorkerContext &resolver = storage.contexts[1]; - resolver.is_resolver = 1; - resolver.resolver_index = 0; - resolver.resolver_count = 1; - resolver.cluster_worker_ids[0] = 0; - resolver.cluster_worker_ids[1] = 1; - resolver.cluster_worker_ids[2] = UINT64_MAX; - auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 0, 0); + SchedulerWorkerContext &scheduler = storage.contexts[1]; + scheduler.is_scheduler = 1; + scheduler.scheduler_index = 0; + scheduler.scheduler_count = 1; + scheduler.cluster_worker_ids[0] = 0; + scheduler.cluster_worker_ids[1] = 1; + scheduler.cluster_worker_ids[2] = UINT64_MAX; + auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 0, 0); scheduler_initialize_free_slot(slot); slot->task_id = 0; slot->gang = 0; scheduler_gm_store( slot->publication, scheduler_dispatch_publication(slot->generation, SchedulerDispatchSlotState::READY) ); - auto *completion_line = scheduler_completion_inbox_at(storage.scheduler_state->base(), &resolver, 0); + auto *completion_line = scheduler_completion_inbox_at(storage.scheduler_state->base(), &scheduler, 0); completion_line->completed_generations[0] = slot->generation; - auto *control = scheduler_task_control_at(storage.scheduler_state->base(), &resolver, 0); + auto *control = scheduler_task_control_at(storage.scheduler_state->base(), &scheduler, 0); control->state = static_cast(SchedulerTaskState::READY); SchedulerWakeStats wake_stats{}; SchedulerReadyStats ready_stats{}; SchedulerCompletionStats completion_stats{}; ASSERT_TRUE(scheduler_service_cluster_completions( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, &wake_stats, &ready_stats, - &completion_stats, nullptr, false, nullptr, nullptr, &storage.owner_states[resolver.inbox_index] + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, &wake_stats, &ready_stats, + &completion_stats, nullptr, false, nullptr, nullptr, &storage.owner_states[scheduler.inbox_index] )); EXPECT_EQ(completion_line->completed_generations[0], 0u); EXPECT_EQ(scheduler_dispatch_state(slot->publication), SchedulerDispatchSlotState::FREE); @@ -267,10 +267,10 @@ TEST(SchedulerClusterCompletion, RejectsStaleCompletionGenerationAtNamedSite) { FixtureStorage storage(1, 3); GraphBuffer graph(1); graph.executable(0, 0); - SchedulerWorkerContext &resolver = storage.contexts[1]; - resolver.is_resolver = 1; - resolver.cluster_worker_ids[0] = 0; - auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 0, 0); + SchedulerWorkerContext &scheduler = storage.contexts[1]; + scheduler.is_scheduler = 1; + scheduler.cluster_worker_ids[0] = 0; + auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 0, 0); scheduler_initialize_free_slot(slot); slot->task_id = 0; scheduler_gm_store( @@ -281,9 +281,9 @@ TEST(SchedulerClusterCompletion, RejectsStaleCompletionGenerationAtNamedSite) { SchedulerReadyStats ready_stats{}; SchedulerCompletionStats completion_stats{}; EXPECT_FALSE(scheduler_service_cluster_completion_slot( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, 0, 0, slot->generation + 1, + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, 0, 0, slot->generation + 1, &wake_stats, &ready_stats, &completion_stats, nullptr, false, nullptr, nullptr, nullptr, - &storage.owner_states[resolver.inbox_index] + &storage.owner_states[scheduler.inbox_index] )); EXPECT_EQ( storage.run_control->error_site, static_cast(SchedulerErrorSite::COMPLETION_GENERATION_MISMATCH) @@ -294,10 +294,10 @@ TEST(SchedulerClusterCompletion, RejectsUnexpectedGangSlotAtNamedSite) { FixtureStorage storage(1, 3); GraphBuffer graph(1); graph.executable(0, 0); - SchedulerWorkerContext &resolver = storage.contexts[1]; - resolver.is_resolver = 1; - resolver.cluster_worker_ids[0] = 0; - auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 0, 0); + SchedulerWorkerContext &scheduler = storage.contexts[1]; + scheduler.is_scheduler = 1; + scheduler.cluster_worker_ids[0] = 0; + auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 0, 0); scheduler_initialize_free_slot(slot); slot->task_id = 0; slot->gang = 1; @@ -309,9 +309,9 @@ TEST(SchedulerClusterCompletion, RejectsUnexpectedGangSlotAtNamedSite) { SchedulerReadyStats ready_stats{}; SchedulerCompletionStats completion_stats{}; EXPECT_FALSE(scheduler_service_cluster_completion_slot( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, 0, 0, slot->generation, + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, 0, 0, slot->generation, &wake_stats, &ready_stats, &completion_stats, nullptr, false, nullptr, nullptr, nullptr, - &storage.owner_states[resolver.inbox_index] + &storage.owner_states[scheduler.inbox_index] )); EXPECT_EQ( storage.run_control->error_site, static_cast(SchedulerErrorSite::COMPLETION_UNEXPECTED_GANG_SLOT) @@ -324,47 +324,47 @@ TEST(SchedulerClusterCompletion, PropagatesTraceToCompletionAndWokenTask) { graph.executable(0, 0); graph.executable(1, 0, {0}); storage.metadata[1].flags |= SCHEDULER_TASK_HAS_FANIN; - SchedulerWorkerContext &resolver = storage.contexts[1]; - resolver.is_resolver = 1; - resolver.resolver_count = 1; - resolver.cluster_worker_ids[0] = 0; - resolver.cluster_worker_ids[1] = 1; - resolver.cluster_worker_ids[2] = 2; - auto *producer = scheduler_task_control_at(storage.scheduler_state->base(), &resolver, 0); + SchedulerWorkerContext &scheduler = storage.contexts[1]; + scheduler.is_scheduler = 1; + scheduler.scheduler_count = 1; + scheduler.cluster_worker_ids[0] = 0; + scheduler.cluster_worker_ids[1] = 1; + scheduler.cluster_worker_ids[2] = 2; + auto *producer = scheduler_task_control_at(storage.scheduler_state->base(), &scheduler, 0); producer->state = static_cast(SchedulerTaskState::READY); SchedulerWakeStats wake_stats{}; ASSERT_EQ( scheduler_bootstrap_route_task( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, 1, &wake_stats + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, 1, &wake_stats ), SchedulerRouteResult::WAITING ); - auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 0, 0); + auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 0, 0); scheduler_initialize_free_slot(slot); slot->task_id = 0; scheduler_gm_store( slot->publication, scheduler_dispatch_publication(slot->generation, SchedulerDispatchSlotState::READY) ); - auto *completion_line = scheduler_completion_inbox_at(storage.scheduler_state->base(), &resolver, 0); + auto *completion_line = scheduler_completion_inbox_at(storage.scheduler_state->base(), &scheduler, 0); completion_line->completed_generations[0] = slot->generation; auto *traces = scheduler_state_at(storage.scheduler_state->base(), storage.layout.trace_cells_offset); producer->completion_resolve_start_cycles = 1; producer->completion_resolve_end_cycles = 1; - producer->resolver_worker_id = UINT64_MAX; + producer->scheduler_worker_id = UINT64_MAX; traces[1].ready_transition_cycles = 1; SchedulerReadyStats ready_stats{}; SchedulerCompletionStats completion_stats{}; ASSERT_TRUE(scheduler_service_cluster_completions( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, &wake_stats, &ready_stats, - &completion_stats, nullptr, true, nullptr, nullptr, &storage.owner_states[resolver.inbox_index] + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, &wake_stats, &ready_stats, + &completion_stats, nullptr, true, nullptr, nullptr, &storage.owner_states[scheduler.inbox_index] )); EXPECT_EQ(producer->completion_resolve_start_cycles, 0u); EXPECT_EQ(producer->completion_resolve_end_cycles, 0u); - EXPECT_EQ(producer->resolver_worker_id, resolver.worker_index); + EXPECT_EQ(producer->scheduler_worker_id, scheduler.worker_index); EXPECT_EQ(traces[1].ready_transition_cycles, 0u); - auto *waiter = scheduler_task_control_at(storage.scheduler_state->base(), &resolver, 1); + auto *waiter = scheduler_task_control_at(storage.scheduler_state->base(), &scheduler, 1); EXPECT_EQ(waiter->state, static_cast(SchedulerTaskState::READY)); } @@ -381,19 +381,19 @@ TEST(SchedulerClusterCompletion, DirectlyRefillsCompletedSlotWhenReadyTaskExists graph.executable(0, 0); graph.executable(1, 0); storage.contexts[0].core_type = static_cast(CoreType::AIC); - SchedulerWorkerContext &resolver = storage.contexts[1]; - resolver.is_resolver = 1; - resolver.resolver_index = 0; - resolver.resolver_count = 1; - resolver.inbox_index = 0; - resolver.cluster_worker_ids[0] = 0; - resolver.cluster_worker_ids[1] = 1; - resolver.cluster_worker_ids[2] = 2; + SchedulerWorkerContext &scheduler = storage.contexts[1]; + scheduler.is_scheduler = 1; + scheduler.scheduler_index = 0; + scheduler.scheduler_count = 1; + scheduler.inbox_index = 0; + scheduler.cluster_worker_ids[0] = 0; + scheduler.cluster_worker_ids[1] = 1; + scheduler.cluster_worker_ids[2] = 2; auto *callables = scheduler_state_at(storage.scheduler_state->base(), storage.layout.callable_addresses_offset); callables[1] = 0x1000; - auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 0, 0); + auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 0, 0); scheduler_initialize_free_slot(slot); const uint32_t completed_generation = slot->generation; slot->task_id = 0; @@ -402,18 +402,18 @@ TEST(SchedulerClusterCompletion, DirectlyRefillsCompletedSlotWhenReadyTaskExists scheduler_gm_store( slot->publication, scheduler_dispatch_publication(completed_generation, SchedulerDispatchSlotState::READY) ); - auto *completion_line = scheduler_completion_inbox_at(storage.scheduler_state->base(), &resolver, 0); + auto *completion_line = scheduler_completion_inbox_at(storage.scheduler_state->base(), &scheduler, 0); completion_line->completed_generations[0] = completed_generation; - auto *completed_control = scheduler_task_control_at(storage.scheduler_state->base(), &resolver, 0); + auto *completed_control = scheduler_task_control_at(storage.scheduler_state->base(), &scheduler, 0); completed_control->state = static_cast(SchedulerTaskState::READY); - auto *ready_control = scheduler_task_control_at(storage.scheduler_state->base(), &resolver, 1); + auto *ready_control = scheduler_task_control_at(storage.scheduler_state->base(), &scheduler, 1); ready_control->state = static_cast(SchedulerTaskState::READY); SchedulerReadyBatch batch{}; SchedulerReadyStats ready_stats{}; - ASSERT_TRUE(scheduler_ready_batch_append(storage.scheduler_state->base(), &resolver, 1, &batch, &ready_stats)); + ASSERT_TRUE(scheduler_ready_batch_append(storage.scheduler_state->base(), &scheduler, 1, &batch, &ready_stats)); ASSERT_TRUE(scheduler_ready_batch_push( - storage.scheduler_state->base(), &resolver, 0, 0, &batch, &ready_stats, - &storage.owner_states[resolver.inbox_index] + storage.scheduler_state->base(), &scheduler, 0, 0, &batch, &ready_stats, + &storage.owner_states[scheduler.inbox_index] )); SchedulerWakeStats wake_stats{}; @@ -421,9 +421,9 @@ TEST(SchedulerClusterCompletion, DirectlyRefillsCompletedSlotWhenReadyTaskExists uint64_t ready_victim_cursors[SCHEDULER_CORE_TYPE_COUNT]{}; uint64_t direct_refilled_slot_mask = 0; ASSERT_TRUE(scheduler_service_cluster_completions( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, &wake_stats, &ready_stats, + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, &wake_stats, &ready_stats, &completion_stats, ready_victim_cursors, false, &direct_refilled_slot_mask, nullptr, - &storage.owner_states[resolver.inbox_index] + &storage.owner_states[scheduler.inbox_index] )); EXPECT_EQ(completion_line->completed_generations[0], 0u); @@ -439,33 +439,33 @@ TEST(SchedulerNormalDispatch, FillsFreshAicSlot) { FixtureStorage storage(1, 3); GraphBuffer graph(1); graph.executable(0, 0); - SchedulerWorkerContext &resolver = storage.contexts[1]; - resolver.is_resolver = 1; - resolver.resolver_count = 1; - resolver.inbox_index = 0; - resolver.cluster_worker_ids[0] = UINT64_MAX; - resolver.cluster_worker_ids[1] = 1; - resolver.cluster_worker_ids[2] = 0; + SchedulerWorkerContext &scheduler = storage.contexts[1]; + scheduler.is_scheduler = 1; + scheduler.scheduler_count = 1; + scheduler.inbox_index = 0; + scheduler.cluster_worker_ids[0] = UINT64_MAX; + scheduler.cluster_worker_ids[1] = 1; + scheduler.cluster_worker_ids[2] = 0; storage.contexts[0].core_type = static_cast(CoreType::AIC); auto *callables = scheduler_state_at(storage.scheduler_state->base(), storage.layout.callable_addresses_offset); callables[1] = 0x1000; - auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 0, 0); + auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 0, 0); scheduler_initialize_free_slot(slot); - auto *control = scheduler_task_control_at(storage.scheduler_state->base(), &resolver, 0); + auto *control = scheduler_task_control_at(storage.scheduler_state->base(), &scheduler, 0); control->state = static_cast(SchedulerTaskState::READY); SchedulerReadyBatch batch{}; SchedulerReadyStats ready_stats{}; - ASSERT_TRUE(scheduler_ready_batch_append(storage.scheduler_state->base(), &resolver, 0, &batch, &ready_stats)); + ASSERT_TRUE(scheduler_ready_batch_append(storage.scheduler_state->base(), &scheduler, 0, &batch, &ready_stats)); ASSERT_TRUE(scheduler_ready_batch_push( - storage.scheduler_state->base(), &resolver, 0, 0, &batch, &ready_stats, - &storage.owner_states[resolver.inbox_index] + storage.scheduler_state->base(), &scheduler, 0, 0, &batch, &ready_stats, + &storage.owner_states[scheduler.inbox_index] )); uint64_t victim_cursors[SCHEDULER_CORE_TYPE_COUNT]{}; ASSERT_TRUE(scheduler_fill_cluster_normal_slots( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, victim_cursors, &ready_stats, - false, 0, nullptr, nullptr, &storage.owner_states[resolver.inbox_index] + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, victim_cursors, &ready_stats, + false, 0, nullptr, nullptr, &storage.owner_states[scheduler.inbox_index] )); EXPECT_EQ(slot->task_id, 0); EXPECT_EQ(scheduler_dispatch_state(slot->publication), SchedulerDispatchSlotState::READY); @@ -476,13 +476,13 @@ TEST(SchedulerNormalDispatch, PreservesProgressWhenALaterFillFails) { GraphBuffer graph(2); graph.executable(0, 0); graph.mixed(1, 3); - SchedulerWorkerContext &resolver = storage.contexts[1]; - resolver.is_resolver = 1; - resolver.resolver_count = 1; - resolver.inbox_index = 0; - resolver.cluster_worker_ids[0] = 0; - resolver.cluster_worker_ids[1] = 1; - resolver.cluster_worker_ids[2] = 2; + SchedulerWorkerContext &scheduler = storage.contexts[1]; + scheduler.is_scheduler = 1; + scheduler.scheduler_count = 1; + scheduler.inbox_index = 0; + scheduler.cluster_worker_ids[0] = 0; + scheduler.cluster_worker_ids[1] = 1; + scheduler.cluster_worker_ids[2] = 2; storage.contexts[0].core_type = static_cast(CoreType::AIC); storage.metadata[1].active_mask = 3; auto *callables = @@ -490,33 +490,33 @@ TEST(SchedulerNormalDispatch, PreservesProgressWhenALaterFillFails) { callables[1] = 0x1000; for (uint32_t slot_index = 0; slot_index < SCHEDULER_PENDING_SLOT_COUNT; ++slot_index) scheduler_initialize_free_slot( - scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 0, slot_index) + scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 0, slot_index) ); SchedulerReadyBatch batch{}; SchedulerReadyStats ready_stats{}; for (int64_t task_id = 0; task_id < 2; ++task_id) { - auto *control = scheduler_task_control_at(storage.scheduler_state->base(), &resolver, task_id); + auto *control = scheduler_task_control_at(storage.scheduler_state->base(), &scheduler, task_id); control->state = static_cast(SchedulerTaskState::READY); ASSERT_TRUE( - scheduler_ready_batch_append(storage.scheduler_state->base(), &resolver, task_id, &batch, &ready_stats) + scheduler_ready_batch_append(storage.scheduler_state->base(), &scheduler, task_id, &batch, &ready_stats) ); } ASSERT_TRUE(scheduler_ready_batch_push( - storage.scheduler_state->base(), &resolver, 0, 0, &batch, &ready_stats, - &storage.owner_states[resolver.inbox_index] + storage.scheduler_state->base(), &scheduler, 0, 0, &batch, &ready_stats, + &storage.owner_states[scheduler.inbox_index] )); uint64_t victim_cursors[SCHEDULER_CORE_TYPE_COUNT]{}; bool failed = false; EXPECT_TRUE(scheduler_fill_cluster_normal_slots( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, victim_cursors, &ready_stats, - false, 0, nullptr, nullptr, &storage.owner_states[resolver.inbox_index], &failed + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, victim_cursors, &ready_stats, + false, 0, nullptr, nullptr, &storage.owner_states[scheduler.inbox_index], &failed )); EXPECT_TRUE(failed); - auto *first_slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 0, 0); + auto *first_slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 0, 0); EXPECT_EQ(first_slot->task_id, 0); EXPECT_EQ(scheduler_dispatch_state(first_slot->publication), SchedulerDispatchSlotState::READY); - auto *failed_slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 0, 1); + auto *failed_slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 0, 1); EXPECT_EQ(failed_slot->task_id, SCHEDULER_TASK_ID_INVALID); EXPECT_EQ(scheduler_dispatch_state(failed_slot->publication), SchedulerDispatchSlotState::FILLING); EXPECT_NE(storage.run_control->scheduler_error, 0u); @@ -528,17 +528,17 @@ TEST(SchedulerNormalDispatch, RejectsExcessAivWorkersAtNamedSite) { GraphBuffer graph(1); graph.executable(0, 1); configure_normal_aiv_cluster(storage, 1); - SchedulerWorkerContext &resolver = storage.contexts[1]; + SchedulerWorkerContext &scheduler = storage.contexts[1]; storage.contexts[0].core_type = static_cast(CoreType::AIV); - enqueue_normal_aiv_tasks(storage, resolver, 0, 1); + enqueue_normal_aiv_tasks(storage, scheduler, 0, 1); uint64_t victim_cursors[SCHEDULER_CORE_TYPE_COUNT]{}; SchedulerReadyStats ready_stats{}; SchedulerDeferredAivQueue deferred{}; bool failed = false; EXPECT_FALSE(scheduler_fill_cluster_normal_slots( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, victim_cursors, &ready_stats, - false, 0, nullptr, &deferred, &storage.owner_states[resolver.inbox_index], &failed + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, victim_cursors, &ready_stats, + false, 0, nullptr, &deferred, &storage.owner_states[scheduler.inbox_index], &failed )); EXPECT_TRUE(failed); EXPECT_EQ( @@ -546,53 +546,53 @@ TEST(SchedulerNormalDispatch, RejectsExcessAivWorkersAtNamedSite) { ); } -TEST(SchedulerNormalDispatch, PublishesOrdinaryAivToPeerBeforeResolver) { +TEST(SchedulerNormalDispatch, PublishesOrdinaryAivToPeerBeforeScheduler) { FixtureStorage storage(1, 3); GraphBuffer graph(1); graph.executable(0, 1); configure_normal_aiv_cluster(storage, 1); - SchedulerWorkerContext &resolver = storage.contexts[1]; - enqueue_normal_aiv_tasks(storage, resolver, 0, 1); + SchedulerWorkerContext &scheduler = storage.contexts[1]; + enqueue_normal_aiv_tasks(storage, scheduler, 0, 1); uint64_t victim_cursors[SCHEDULER_CORE_TYPE_COUNT]{}; SchedulerReadyStats ready_stats{}; SchedulerDeferredAivQueue deferred{}; ASSERT_TRUE(scheduler_fill_cluster_normal_slots( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, victim_cursors, &ready_stats, - false, 0, nullptr, &deferred, &storage.owner_states[resolver.inbox_index] + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, victim_cursors, &ready_stats, + false, 0, nullptr, &deferred, &storage.owner_states[scheduler.inbox_index] )); - auto *peer_slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 2, 0); + auto *peer_slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 2, 0); EXPECT_EQ(peer_slot->task_id, 0); EXPECT_EQ(scheduler_dispatch_state(peer_slot->publication), SchedulerDispatchSlotState::READY); EXPECT_EQ(deferred.count, 0u); } -TEST(SchedulerDeferredAiv, ReservesOnlyAvailableResolverSlotsBeforeClaiming) { +TEST(SchedulerDeferredAiv, ReservesOnlyAvailableSchedulerSlotsBeforeClaiming) { FixtureStorage storage(3, 3); GraphBuffer graph(3); for (uint64_t task = 0; task < 3; ++task) graph.executable(task, 1); configure_normal_aiv_cluster(storage, 3); - SchedulerWorkerContext &resolver = storage.contexts[1]; - occupy_normal_slot(storage, resolver, 2, 0, SCHEDULER_TASK_ID_INVALID); - occupy_normal_slot(storage, resolver, 2, 1, SCHEDULER_TASK_ID_INVALID); - enqueue_normal_aiv_tasks(storage, resolver, 0, 3); + SchedulerWorkerContext &scheduler = storage.contexts[1]; + occupy_normal_slot(storage, scheduler, 2, 0, SCHEDULER_TASK_ID_INVALID); + occupy_normal_slot(storage, scheduler, 2, 1, SCHEDULER_TASK_ID_INVALID); + enqueue_normal_aiv_tasks(storage, scheduler, 0, 3); uint64_t victim_cursors[SCHEDULER_CORE_TYPE_COUNT]{}; SchedulerReadyStats ready_stats{}; SchedulerDeferredAivQueue deferred{}; ASSERT_TRUE(scheduler_fill_cluster_normal_slots( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, victim_cursors, &ready_stats, - false, 0, nullptr, &deferred, &storage.owner_states[resolver.inbox_index] + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, victim_cursors, &ready_stats, + false, 0, nullptr, &deferred, &storage.owner_states[scheduler.inbox_index] )); ASSERT_EQ(deferred.count, SCHEDULER_PENDING_SLOT_COUNT); for (uint32_t slot_index = 0; slot_index < SCHEDULER_PENDING_SLOT_COUNT; ++slot_index) { - auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 1, slot_index); + auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 1, slot_index); EXPECT_EQ(scheduler_dispatch_state(slot->publication), SchedulerDispatchSlotState::FILLING); EXPECT_EQ(slot->task_id, SCHEDULER_TASK_ID_INVALID); } - auto *ready_inbox = scheduler_ready_inbox_at(storage.scheduler_state->base(), &resolver, 1, 0); + auto *ready_inbox = scheduler_ready_inbox_at(storage.scheduler_state->base(), &scheduler, 1, 0); EXPECT_NE(ready_inbox->head, SCHEDULER_INBOX_EMPTY); } @@ -601,15 +601,15 @@ TEST(SchedulerDeferredAiv, RejectsInvalidReservationBeforeSlotAccess) { GraphBuffer graph(1); graph.executable(0, 1); configure_normal_aiv_cluster(storage, 1); - SchedulerWorkerContext &resolver = storage.contexts[1]; + SchedulerWorkerContext &scheduler = storage.contexts[1]; SchedulerDeferredAivQueue deferred{}; deferred.count = 1; deferred.entries[0].ready.task_id = 0; deferred.entries[0].reserved_slot = {UINT64_MAX, UINT32_MAX, 1}; uint32_t published_slot = 0; - EXPECT_FALSE(scheduler_publish_deferred_aiv_to_resolver( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, &deferred, false, + EXPECT_FALSE(scheduler_publish_deferred_aiv_local( + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, &deferred, false, &published_slot )); EXPECT_EQ(published_slot, UINT32_MAX); @@ -623,8 +623,8 @@ TEST(SchedulerDeferredAiv, RejectsReservationWithMaterializedTask) { GraphBuffer graph(1); graph.executable(0, 1); configure_normal_aiv_cluster(storage, 1); - SchedulerWorkerContext &resolver = storage.contexts[1]; - auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 1, 0); + SchedulerWorkerContext &scheduler = storage.contexts[1]; + auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 1, 0); slot->task_id = 0; scheduler_gm_store( slot->publication, scheduler_dispatch_publication(slot->generation, SchedulerDispatchSlotState::FILLING) @@ -632,11 +632,11 @@ TEST(SchedulerDeferredAiv, RejectsReservationWithMaterializedTask) { SchedulerDeferredAivQueue deferred{}; deferred.count = 1; deferred.entries[0].ready.task_id = 0; - deferred.entries[0].reserved_slot = {resolver.worker_index, 0, slot->generation}; + deferred.entries[0].reserved_slot = {scheduler.worker_index, 0, slot->generation}; uint32_t published_slot = 0; - EXPECT_FALSE(scheduler_publish_deferred_aiv_to_resolver( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, &deferred, false, + EXPECT_FALSE(scheduler_publish_deferred_aiv_local( + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, &deferred, false, &published_slot )); EXPECT_EQ(published_slot, UINT32_MAX); @@ -650,10 +650,10 @@ TEST(SchedulerDeferredAiv, RejectsReservationOwnedByPeerAtNamedSite) { GraphBuffer graph(1); graph.executable(0, 1); configure_normal_aiv_cluster(storage, 1); - SchedulerWorkerContext &resolver = storage.contexts[1]; + SchedulerWorkerContext &scheduler = storage.contexts[1]; EXPECT_FALSE(scheduler_release_deferred_aiv_reservation( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, SchedulerFreeSlotClaim{2, 0, 1} + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, SchedulerFreeSlotClaim{2, 0, 1} )); EXPECT_EQ( storage.run_control->error_site, static_cast(SchedulerErrorSite::DEFERRED_RESERVATION_INVALID_OWNER) @@ -665,73 +665,73 @@ TEST(SchedulerDeferredAiv, RejectsReservationWithInvalidStateAtNamedSite) { GraphBuffer graph(1); graph.executable(0, 1); configure_normal_aiv_cluster(storage, 1); - SchedulerWorkerContext &resolver = storage.contexts[1]; - auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 1, 0); + SchedulerWorkerContext &scheduler = storage.contexts[1]; + auto *slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 1, 0); EXPECT_FALSE(scheduler_release_deferred_aiv_reservation( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, - SchedulerFreeSlotClaim{resolver.worker_index, 0, slot->generation} + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, + SchedulerFreeSlotClaim{scheduler.worker_index, 0, slot->generation} )); EXPECT_EQ( storage.run_control->error_site, static_cast(SchedulerErrorSite::DEFERRED_RESERVATION_INVALID_STATE) ); } -TEST(SchedulerDeferredAiv, DoesNotClaimWithoutResolverReservation) { +TEST(SchedulerDeferredAiv, DoesNotClaimWithoutSchedulerReservation) { FixtureStorage storage(1, 3); GraphBuffer graph(1); graph.executable(0, 1); configure_normal_aiv_cluster(storage, 1); - SchedulerWorkerContext &resolver = storage.contexts[1]; + SchedulerWorkerContext &scheduler = storage.contexts[1]; for (uint64_t worker = 1; worker <= 2; ++worker) { for (uint32_t slot = 0; slot < SCHEDULER_PENDING_SLOT_COUNT; ++slot) - occupy_normal_slot(storage, resolver, worker, slot, SCHEDULER_TASK_ID_INVALID); + occupy_normal_slot(storage, scheduler, worker, slot, SCHEDULER_TASK_ID_INVALID); } - enqueue_normal_aiv_tasks(storage, resolver, 0, 1); + enqueue_normal_aiv_tasks(storage, scheduler, 0, 1); uint64_t victim_cursors[SCHEDULER_CORE_TYPE_COUNT]{}; SchedulerReadyStats ready_stats{}; SchedulerDeferredAivQueue deferred{}; bool failed = true; EXPECT_FALSE(scheduler_fill_cluster_normal_slots( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, victim_cursors, &ready_stats, - false, 0, nullptr, &deferred, &storage.owner_states[resolver.inbox_index], &failed + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, victim_cursors, &ready_stats, + false, 0, nullptr, &deferred, &storage.owner_states[scheduler.inbox_index], &failed )); EXPECT_FALSE(failed); EXPECT_EQ(deferred.count, 0u); - auto *ready_inbox = scheduler_ready_inbox_at(storage.scheduler_state->base(), &resolver, 1, 0); + auto *ready_inbox = scheduler_ready_inbox_at(storage.scheduler_state->base(), &scheduler, 1, 0); EXPECT_EQ(ready_inbox->head, 0); } -TEST(SchedulerDeferredAiv, KeepsReservationForResolverWhenNoPeerIsActive) { +TEST(SchedulerDeferredAiv, KeepsReservationForSchedulerWhenNoPeerIsActive) { FixtureStorage storage(1, 3); GraphBuffer graph(1); graph.executable(0, 1); configure_normal_aiv_cluster(storage, 1); - SchedulerWorkerContext &resolver = storage.contexts[1]; - resolver.cluster_worker_ids[2] = UINT64_MAX; - enqueue_normal_aiv_tasks(storage, resolver, 0, 1); + SchedulerWorkerContext &scheduler = storage.contexts[1]; + scheduler.cluster_worker_ids[2] = UINT64_MAX; + enqueue_normal_aiv_tasks(storage, scheduler, 0, 1); uint64_t victim_cursors[SCHEDULER_CORE_TYPE_COUNT]{}; SchedulerReadyStats ready_stats{}; SchedulerDeferredAivQueue deferred{}; ASSERT_TRUE(scheduler_fill_cluster_normal_slots( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, victim_cursors, &ready_stats, - false, 0, nullptr, &deferred, &storage.owner_states[resolver.inbox_index] + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, victim_cursors, &ready_stats, + false, 0, nullptr, &deferred, &storage.owner_states[scheduler.inbox_index] )); ASSERT_EQ(deferred.count, 1u); SchedulerWakeStats wake_stats{}; SchedulerCompletionStats completion_stats{}; EXPECT_TRUE(scheduler_drain_deferred_aiv_to_peer( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, &deferred, &wake_stats, - &ready_stats, &completion_stats, false, nullptr, nullptr, &storage.owner_states[resolver.inbox_index] + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, &deferred, &wake_stats, + &ready_stats, &completion_stats, false, nullptr, nullptr, &storage.owner_states[scheduler.inbox_index] )); EXPECT_EQ(deferred.count, 1u); uint32_t self_slot = UINT32_MAX; - ASSERT_TRUE(scheduler_publish_deferred_aiv_to_resolver( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, &deferred, false, &self_slot + ASSERT_TRUE(scheduler_publish_deferred_aiv_local( + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, &deferred, false, &self_slot )); EXPECT_EQ(deferred.count, 0u); EXPECT_LT(self_slot, SCHEDULER_PENDING_SLOT_COUNT); @@ -743,21 +743,21 @@ TEST(SchedulerDeferredAiv, PrefersNewPeerCapacityAndSelfPublishesOnlyOne) { for (uint64_t task = 0; task < 2; ++task) graph.executable(task, 1); configure_normal_aiv_cluster(storage, 2); - SchedulerWorkerContext &resolver = storage.contexts[1]; - occupy_normal_slot(storage, resolver, 2, 0, SCHEDULER_TASK_ID_INVALID); - occupy_normal_slot(storage, resolver, 2, 1, SCHEDULER_TASK_ID_INVALID); - enqueue_normal_aiv_tasks(storage, resolver, 0, 2); + SchedulerWorkerContext &scheduler = storage.contexts[1]; + occupy_normal_slot(storage, scheduler, 2, 0, SCHEDULER_TASK_ID_INVALID); + occupy_normal_slot(storage, scheduler, 2, 1, SCHEDULER_TASK_ID_INVALID); + enqueue_normal_aiv_tasks(storage, scheduler, 0, 2); uint64_t victim_cursors[SCHEDULER_CORE_TYPE_COUNT]{}; SchedulerReadyStats ready_stats{}; SchedulerDeferredAivQueue deferred{}; ASSERT_TRUE(scheduler_fill_cluster_normal_slots( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, victim_cursors, &ready_stats, - false, 0, nullptr, &deferred, &storage.owner_states[resolver.inbox_index] + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, victim_cursors, &ready_stats, + false, 0, nullptr, &deferred, &storage.owner_states[scheduler.inbox_index] )); ASSERT_EQ(deferred.count, 2u); const SchedulerFreeSlotClaim first_reservation = deferred.entries[0].reserved_slot; - auto *peer_slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 2, 0); + auto *peer_slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 2, 0); peer_slot->task_id = SCHEDULER_TASK_ID_INVALID; scheduler_gm_store( peer_slot->publication, scheduler_dispatch_publication(peer_slot->generation, SchedulerDispatchSlotState::FREE) @@ -765,23 +765,23 @@ TEST(SchedulerDeferredAiv, PrefersNewPeerCapacityAndSelfPublishesOnlyOne) { SchedulerWakeStats wake_stats{}; SchedulerCompletionStats completion_stats{}; ASSERT_TRUE(scheduler_drain_deferred_aiv_to_peer( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, &deferred, &wake_stats, - &ready_stats, &completion_stats, false, nullptr, nullptr, &storage.owner_states[resolver.inbox_index] + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, &deferred, &wake_stats, + &ready_stats, &completion_stats, false, nullptr, nullptr, &storage.owner_states[scheduler.inbox_index] )); ASSERT_EQ(deferred.count, 1u); EXPECT_EQ(scheduler_dispatch_state(peer_slot->publication), SchedulerDispatchSlotState::READY); auto *released = scheduler_dispatch_slot_at( - storage.scheduler_state->base(), &resolver, first_reservation.worker_id, first_reservation.slot_index + storage.scheduler_state->base(), &scheduler, first_reservation.worker_id, first_reservation.slot_index ); EXPECT_EQ(scheduler_dispatch_state(released->publication), SchedulerDispatchSlotState::FREE); uint32_t self_slot = UINT32_MAX; - ASSERT_TRUE(scheduler_publish_deferred_aiv_to_resolver( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, &deferred, false, &self_slot + ASSERT_TRUE(scheduler_publish_deferred_aiv_local( + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, &deferred, false, &self_slot )); EXPECT_EQ(deferred.count, 0u); ASSERT_LT(self_slot, SCHEDULER_PENDING_SLOT_COUNT); - auto *published = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 1, self_slot); + auto *published = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 1, self_slot); EXPECT_EQ(scheduler_dispatch_state(published->publication), SchedulerDispatchSlotState::READY); } @@ -791,29 +791,29 @@ TEST(SchedulerDeferredAiv, KeepsSecondReservationAfterOneSelfPublish) { for (uint64_t task = 0; task < 2; ++task) graph.executable(task, 1); configure_normal_aiv_cluster(storage, 2); - SchedulerWorkerContext &resolver = storage.contexts[1]; - occupy_normal_slot(storage, resolver, 2, 0, SCHEDULER_TASK_ID_INVALID); - occupy_normal_slot(storage, resolver, 2, 1, SCHEDULER_TASK_ID_INVALID); - enqueue_normal_aiv_tasks(storage, resolver, 0, 2); + SchedulerWorkerContext &scheduler = storage.contexts[1]; + occupy_normal_slot(storage, scheduler, 2, 0, SCHEDULER_TASK_ID_INVALID); + occupy_normal_slot(storage, scheduler, 2, 1, SCHEDULER_TASK_ID_INVALID); + enqueue_normal_aiv_tasks(storage, scheduler, 0, 2); uint64_t victim_cursors[SCHEDULER_CORE_TYPE_COUNT]{}; SchedulerReadyStats ready_stats{}; SchedulerDeferredAivQueue deferred{}; ASSERT_TRUE(scheduler_fill_cluster_normal_slots( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, victim_cursors, &ready_stats, - false, 0, nullptr, &deferred, &storage.owner_states[resolver.inbox_index] + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, victim_cursors, &ready_stats, + false, 0, nullptr, &deferred, &storage.owner_states[scheduler.inbox_index] )); ASSERT_EQ(deferred.count, 2u); uint32_t self_slot = UINT32_MAX; - ASSERT_TRUE(scheduler_publish_deferred_aiv_to_resolver( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, &deferred, false, &self_slot + ASSERT_TRUE(scheduler_publish_deferred_aiv_local( + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, &deferred, false, &self_slot )); ASSERT_EQ(deferred.count, 1u); - auto *published = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 1, self_slot); + auto *published = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 1, self_slot); EXPECT_EQ(scheduler_dispatch_state(published->publication), SchedulerDispatchSlotState::READY); const SchedulerFreeSlotClaim remaining = deferred.entries[0].reserved_slot; auto *reserved = scheduler_dispatch_slot_at( - storage.scheduler_state->base(), &resolver, remaining.worker_id, remaining.slot_index + storage.scheduler_state->base(), &scheduler, remaining.worker_id, remaining.slot_index ); EXPECT_EQ(scheduler_dispatch_state(reserved->publication), SchedulerDispatchSlotState::FILLING); EXPECT_EQ(reserved->task_id, SCHEDULER_TASK_ID_INVALID); @@ -825,32 +825,32 @@ TEST(SchedulerDeferredAiv, RetiresCompletedPeerAndRefillsWithoutFreeDecision) { graph.executable(0, 1); graph.executable(1, 1); configure_normal_aiv_cluster(storage, 2); - SchedulerWorkerContext &resolver = storage.contexts[1]; - occupy_normal_slot(storage, resolver, 2, 0, 0); - occupy_normal_slot(storage, resolver, 2, 1, SCHEDULER_TASK_ID_INVALID); - occupy_normal_slot(storage, resolver, 1, 1, SCHEDULER_TASK_ID_INVALID); - auto *completed_control = scheduler_task_control_at(storage.scheduler_state->base(), &resolver, 0); + SchedulerWorkerContext &scheduler = storage.contexts[1]; + occupy_normal_slot(storage, scheduler, 2, 0, 0); + occupy_normal_slot(storage, scheduler, 2, 1, SCHEDULER_TASK_ID_INVALID); + occupy_normal_slot(storage, scheduler, 1, 1, SCHEDULER_TASK_ID_INVALID); + auto *completed_control = scheduler_task_control_at(storage.scheduler_state->base(), &scheduler, 0); completed_control->state = static_cast(SchedulerTaskState::READY); - enqueue_normal_aiv_tasks(storage, resolver, 1, 2); + enqueue_normal_aiv_tasks(storage, scheduler, 1, 2); uint64_t victim_cursors[SCHEDULER_CORE_TYPE_COUNT]{}; SchedulerReadyStats ready_stats{}; SchedulerDeferredAivQueue deferred{}; ASSERT_TRUE(scheduler_fill_cluster_normal_slots( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, victim_cursors, &ready_stats, - false, 0, nullptr, &deferred, &storage.owner_states[resolver.inbox_index] + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, victim_cursors, &ready_stats, + false, 0, nullptr, &deferred, &storage.owner_states[scheduler.inbox_index] )); ASSERT_EQ(deferred.count, 1u); - auto *peer_slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 2, 0); + auto *peer_slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 2, 0); const uint32_t completed_generation = peer_slot->generation; - auto *completion_line = scheduler_completion_inbox_at(storage.scheduler_state->base(), &resolver, 2); + auto *completion_line = scheduler_completion_inbox_at(storage.scheduler_state->base(), &scheduler, 2); completion_line->completed_generations[0] = completed_generation; SchedulerWakeStats wake_stats{}; SchedulerCompletionStats completion_stats{}; ASSERT_TRUE(scheduler_drain_deferred_aiv_to_peer( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, &deferred, &wake_stats, - &ready_stats, &completion_stats, false, nullptr, nullptr, &storage.owner_states[resolver.inbox_index] + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, &deferred, &wake_stats, + &ready_stats, &completion_stats, false, nullptr, nullptr, &storage.owner_states[scheduler.inbox_index] )); EXPECT_EQ(deferred.count, 0u); EXPECT_EQ(completion_line->completed_generations[0], 0u); @@ -861,20 +861,20 @@ TEST(SchedulerDeferredAiv, RetiresCompletedPeerAndRefillsWithoutFreeDecision) { EXPECT_EQ(scheduler_dispatch_state(peer_slot->publication), SchedulerDispatchSlotState::READY); } -TEST(SchedulerDeferredAiv, ResolverCompletionDoesNotDirectRefillItself) { +TEST(SchedulerDeferredAiv, SchedulerCompletionDoesNotDirectRefillItself) { FixtureStorage storage(2, 3); GraphBuffer graph(2); graph.executable(0, 1); graph.executable(1, 1); configure_normal_aiv_cluster(storage, 2); - SchedulerWorkerContext &resolver = storage.contexts[1]; - occupy_normal_slot(storage, resolver, 1, 0, 0); - auto *completed_slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &resolver, 1, 0); - auto *completion_line = scheduler_completion_inbox_at(storage.scheduler_state->base(), &resolver, 1); + SchedulerWorkerContext &scheduler = storage.contexts[1]; + occupy_normal_slot(storage, scheduler, 1, 0, 0); + auto *completed_slot = scheduler_dispatch_slot_at(storage.scheduler_state->base(), &scheduler, 1, 0); + auto *completion_line = scheduler_completion_inbox_at(storage.scheduler_state->base(), &scheduler, 1); completion_line->completed_generations[0] = completed_slot->generation; - auto *completed_control = scheduler_task_control_at(storage.scheduler_state->base(), &resolver, 0); + auto *completed_control = scheduler_task_control_at(storage.scheduler_state->base(), &scheduler, 0); completed_control->state = static_cast(SchedulerTaskState::READY); - enqueue_normal_aiv_tasks(storage, resolver, 1, 2); + enqueue_normal_aiv_tasks(storage, scheduler, 1, 2); SchedulerWakeStats wake_stats{}; SchedulerReadyStats ready_stats{}; @@ -882,14 +882,14 @@ TEST(SchedulerDeferredAiv, ResolverCompletionDoesNotDirectRefillItself) { uint64_t victim_cursors[SCHEDULER_CORE_TYPE_COUNT]{}; uint64_t direct_refilled_slot_mask = 0; ASSERT_TRUE(scheduler_service_cluster_completions( - graph.graph(), storage.scheduler_state->base(), &resolver, storage.run_control, &wake_stats, &ready_stats, + graph.graph(), storage.scheduler_state->base(), &scheduler, storage.run_control, &wake_stats, &ready_stats, &completion_stats, victim_cursors, false, &direct_refilled_slot_mask, nullptr, - &storage.owner_states[resolver.inbox_index] + &storage.owner_states[scheduler.inbox_index] )); EXPECT_EQ(direct_refilled_slot_mask, 0u); EXPECT_EQ(completed_slot->task_id, SCHEDULER_TASK_ID_INVALID); EXPECT_EQ(scheduler_dispatch_state(completed_slot->publication), SchedulerDispatchSlotState::FREE); - auto *ready_inbox = scheduler_ready_inbox_at(storage.scheduler_state->base(), &resolver, 1, 0); + auto *ready_inbox = scheduler_ready_inbox_at(storage.scheduler_state->base(), &scheduler, 1, 0); EXPECT_EQ(ready_inbox->head, 1); } diff --git a/tests/ut/cpp/a5/test_hbg_scheduler_ready.cpp b/tests/ut/cpp/a5/test_hbg_scheduler_ready.cpp index 9518b03d82..b696b846d8 100644 --- a/tests/ut/cpp/a5/test_hbg_scheduler_ready.cpp +++ b/tests/ut/cpp/a5/test_hbg_scheduler_ready.cpp @@ -123,7 +123,7 @@ struct FixtureStorage { owner_states = scheduler_state_at(scheduler_state->base(), layout.ready_owner_states_offset); run_control->aiv_active_worker_count = workers; - run_control->resolver_count = workers; + run_control->scheduler_count = workers; for (uint64_t worker = 0; worker < workers; ++worker) { SchedulerWorkerContext &context = contexts[worker]; context.core_type = static_cast(CoreType::AIV); @@ -248,7 +248,7 @@ TEST(SchedulerBootstrap, PublishesExclusiveInboxAndAggregatesDirectory) { EXPECT_EQ(stats.batch_count, 1u); } -TEST(SchedulerReadyInbox, RejectsResolverCapacityBoundary) { +TEST(SchedulerReadyInbox, RejectsSchedulerCapacityBoundary) { FixtureStorage storage(1, 1); SchedulerReadyStats stats{}; SchedulerReadyBatch batch{}; @@ -256,20 +256,19 @@ TEST(SchedulerReadyInbox, RejectsResolverCapacityBoundary) { uint64_t ready_types = 0; EXPECT_FALSE(scheduler_bootstrap_ready_batch_publish( - storage.scheduler_state->base(), &storage.contexts[0], 0, SCHEDULER_RESOLVER_CAPACITY, &batch, &stats, - &ready_types + storage.scheduler_state->base(), &storage.contexts[0], 0, SCHEDULER_CAPACITY, &batch, &stats, &ready_types )); - storage.contexts[0].inbox_index = SCHEDULER_RESOLVER_CAPACITY; + storage.contexts[0].inbox_index = SCHEDULER_CAPACITY; EXPECT_FALSE(scheduler_ready_owner_maintain_type( storage.scheduler_state->base(), &storage.contexts[0], 0, &storage.owner_states[0] )); EXPECT_FALSE(scheduler_ready_batch_push( - storage.scheduler_state->base(), &storage.contexts[0], 0, SCHEDULER_RESOLVER_CAPACITY, &batch, &stats, + storage.scheduler_state->base(), &storage.contexts[0], 0, SCHEDULER_CAPACITY, &batch, &stats, &storage.owner_states[0] )); storage.contexts[0].inbox_index = 0; EXPECT_FALSE(scheduler_bootstrap_ready_directory_publish( - storage.scheduler_state->base(), &storage.contexts[0], SCHEDULER_RESOLVER_CAPACITY + 1 + storage.scheduler_state->base(), &storage.contexts[0], SCHEDULER_CAPACITY + 1 )); } @@ -512,7 +511,7 @@ TEST(SchedulerReadyInbox, StealsOnlyFromMarkedVictim) { EXPECT_EQ(stats.steal_count, 1u); } -TEST(SchedulerReadyInbox, DirectoryShardIgnoresResolverTail) { +TEST(SchedulerReadyInbox, DirectoryShardIgnoresSchedulerTail) { FixtureStorage storage(1, 9); auto *directory = scheduler_ready_directory_at(storage.scheduler_state->base(), &storage.contexts[0]); directory->core_types[0][1].bits = UINT64_C(1) << 6; @@ -1080,7 +1079,7 @@ TEST(SchedulerReadyWake, ConcurrentRegistrationAndCloseResolveEveryConsumerExact } } -TEST(SchedulerReadyWake, WakeResolvePublishesConsumerToResolverLocalInbox) { +TEST(SchedulerReadyWake, WakeResolvePublishesConsumerToSchedulerLocalInbox) { FixtureStorage storage(2, 1); GraphBuffer graph(2); graph.executable(0, 0);