diff --git a/src/kernbench/components/builtin/pe_cpu.py b/src/kernbench/components/builtin/pe_cpu.py index 7b923fe..752ffeb 100644 --- a/src/kernbench/components/builtin/pe_cpu.py +++ b/src/kernbench/components/builtin/pe_cpu.py @@ -117,17 +117,13 @@ class PeCpuComponent(ComponentBase): pe_exec_start = env.now scheduler_id = f"{self._pe_prefix}.pe_scheduler" - # Choose execution mode: greenlet (ADR-0020) or legacy command-list + # ADR-0020 greenlet execution — always used so IPCQ / ring credits / + # fabric-transfer sim events fire regardless of data-mode. KernelRunner + # guards its store reads on ``self._store is not None``. store = getattr(self.ctx, "memory_store", None) if self.ctx else None - - if store is not None: - composite_results = yield from self._execute_greenlet( - env, kernel_fn, kernel_args, num_programs, scheduler_id, store, - ) - else: - composite_results = yield from self._execute_legacy( - env, kernel_fn, kernel_args, num_programs, scheduler_id, - ) + composite_results = yield from self._execute_greenlet( + env, kernel_fn, kernel_args, num_programs, scheduler_id, store, + ) # Record PE-internal execution time txn.result_data["pe_exec_ns"] = env.now - pe_exec_start diff --git a/src/kernbench/runtime_api/context.py b/src/kernbench/runtime_api/context.py index 97d2855..08b6716 100644 --- a/src/kernbench/runtime_api/context.py +++ b/src/kernbench/runtime_api/context.py @@ -570,8 +570,14 @@ class RuntimeContext: h = self.submit(msg) self.wait(h) - # Submit MemoryWriteMsg per shard (deploy data to device) - if pattern is not None: + # Submit MemoryWriteMsg per shard (deploy data to device). Gated on + # memory_store presence: under enable_data=False there is no + # MemoryStore to populate and no Phase 2 DataExecutor replay, so the + # per-shard sim events are pure wall-clock overhead with no effect + # on reported kernel latency (Yangwook's max(t_end) - min(t_start) + # formula excludes ops before the kernel starts). + store = getattr(self.engine, "_memory_store", None) + if pattern is not None and store is not None: for shard in handle.shards: h = self.submit(MemoryWriteMsg( correlation_id=self.correlation_id, @@ -591,8 +597,7 @@ class RuntimeContext: # VA; Phase 2 DataExecutor reads via the addresses captured in # op_log (VA for tl.load). Without this, zero-init tensors are # invisible to kernels in Phase 2. - store = getattr(self.engine, "_memory_store", None) - if store is not None and pattern == "zero" and handle.va_base: + if pattern == "zero" and handle.va_base: import numpy as np from kernbench.runtime_api.tensor import _numpy_dtype np_dtype = _numpy_dtype(dtype) diff --git a/src/kernbench/sim_engine/engine.py b/src/kernbench/sim_engine/engine.py index 8fc3bd1..03fa29d 100644 --- a/src/kernbench/sim_engine/engine.py +++ b/src/kernbench/sim_engine/engine.py @@ -53,14 +53,16 @@ class GraphEngine: self._events: dict[str, simpy.Event] = {} self._counter = 0 overrides = component_overrides or {} - # ADR-0020: optional data execution support - self._op_logger = None + # ADR-0020: optional data execution support. OpLogger is always + # created so op_log-based latency (max(t_end) - min(t_start)) is + # available in both modes; MemoryStore is created only when + # enable_data=True so Phase 2 DataExecutor replay is gated on it. self._memory_store = None if enable_data: from kernbench.sim_engine.memory_store import MemoryStore - from kernbench.sim_engine.op_log import OpLogger self._memory_store = MemoryStore() - self._op_logger = OpLogger(memory_store=self._memory_store) + from kernbench.sim_engine.op_log import OpLogger + self._op_logger = OpLogger(memory_store=self._memory_store) # Cursor for incremental Phase 2 replay (ADR-0020 D6). # SimPy env.now is monotonic so newly logged records always sort # to the tail; the cursor remains valid across waits.