-
Notifications
You must be signed in to change notification settings - Fork 67
Add: eager prebuilt runtime-arena prewarm at worker init #1322
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -219,6 +219,12 @@ def my_l4_orch(orch, args, config): | |
| _CTRL_MAP_HOST = 14 | ||
| _CTRL_UNMAP_HOST = 15 | ||
|
|
||
| # Eagerly build + cache the prebuilt runtime-arena image for a run config's ring | ||
| # sizing so the first run() with the same sizing skips the (~800ms) cold build. | ||
| # Sent from the parent at end of init() alongside CTRL_PREPARE. Carries the | ||
| # packed CallConfig at _OFF_CONFIG (same slot a task dispatch uses). | ||
| _CTRL_PREWARM_CONFIG = 16 | ||
|
|
||
| # MAP_HOST payload: token (u64), parent_va (u64), nbytes (u64), then the | ||
| # NUL-free host-buffer shm name as the trailing bytes. UNMAP_HOST payload is the | ||
| # token alone. | ||
|
|
@@ -1226,6 +1232,12 @@ def _run_chip_main_loop( # noqa: PLR0912, PLR0913, PLR0915 -- unified TASK_READ | |
| f"prepare chip={device_id}: callable hash {_format_digest(digest)} not registered" | ||
| ) | ||
| _ensure_prepared(cw, registry, prepared, int(cid), device_id=device_id) | ||
| elif sub_cmd == _CTRL_PREWARM_CONFIG: | ||
| # Config-only: build + cache the prebuilt runtime arena for | ||
| # this run config's ring sizing so the first run() hits the | ||
| # cache. No callable digest — the arena is callable-agnostic. | ||
| cfg = _read_config_from_mailbox(buf) | ||
| cw.prewarm_config(cfg) | ||
| elif sub_cmd == _CTRL_REGISTER: | ||
| digest = _read_control_digest(buf) | ||
| payload_size = struct.unpack_from("Q", buf, _CTRL_OFF_ARG0)[0] | ||
|
|
@@ -1566,6 +1578,11 @@ def __init__( | |
| self._hierarchical_start_mu = threading.Lock() | ||
| self._hierarchical_start_cv = threading.Condition(self._hierarchical_start_mu) | ||
|
|
||
| # Optional CallConfig whose ring sizing is pre-warmed at init() so the | ||
| # first run() with the same sizing skips the (~800ms) cold prebuilt | ||
| # runtime-arena build. Set by init(prewarm_config=...); None = disabled. | ||
| self._prewarm_config: Any | None = None | ||
|
|
||
| # Level-2 internals | ||
| self._chip_worker: ChipWorker | None = None | ||
|
|
||
|
|
@@ -3046,9 +3063,20 @@ def add_worker(self, worker: Worker) -> int: | |
| # init — auto-discovery | ||
| # ------------------------------------------------------------------ | ||
|
|
||
| def init(self) -> None: | ||
| def init(self, prewarm_config=None) -> None: | ||
| """Initialize the worker. | ||
|
|
||
| Args: | ||
| prewarm_config: Optional CallConfig. When given, its ring sizing | ||
| (``runtime_env.ring_task_window`` / ``ring_heap`` / | ||
| ``ring_dep_pool``) is eagerly built + cached at init so the first | ||
| ``run`` with the same sizing skips the (~800ms) cold prebuilt | ||
| runtime-arena build. A no-op for runtimes without a prebuilt arena | ||
| (host_build_graph). ``None`` (default) disables prewarm. | ||
| """ | ||
| if self._initialized: | ||
| raise RuntimeError("Worker already initialized") | ||
| self._prewarm_config = prewarm_config | ||
|
Comment on lines
3077
to
+3079
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To ensure consistent and early error reporting, we should validate the if self._initialized:
raise RuntimeError("Worker already initialized")
if prewarm_config is not None:
prewarm_config.validate()
self._prewarm_config = prewarm_config
Comment on lines
+3066
to
+3079
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win Make L3+ prewarming happen during For hierarchical workers, 🤖 Prompt for AI Agents |
||
|
|
||
| try: | ||
| if self.level == 2: | ||
|
|
@@ -3083,6 +3111,11 @@ def _init_level2(self) -> None: | |
| if isinstance(target, ChipCallable): | ||
| self._chip_worker._register_callable_at_slot(cid, target) | ||
|
|
||
| # Pre-warm the prebuilt runtime-arena cache for the declared config so the | ||
| # first run() with matching ring sizing skips the cold arena build. | ||
| if self._prewarm_config is not None: | ||
| self._chip_worker.prewarm_config(self._prewarm_config) | ||
|
|
||
| def _init_hierarchical(self) -> None: | ||
| device_ids = self._config.get("device_ids", []) | ||
| n_sub = self._config.get("num_sub_workers", 0) | ||
|
|
@@ -3311,6 +3344,14 @@ def _start_hierarchical(self) -> None: # noqa: PLR0912 -- three parallel fork l | |
| for worker_id in range(len(self._chip_shms)): | ||
| dw.control_prepare(worker_id, digest) | ||
|
|
||
| # Pre-warm each chip child's prebuilt runtime-arena cache for the | ||
| # declared config so the first dispatch with matching ring sizing | ||
| # skips the (~800ms) cold arena build. No-op when no prewarm config | ||
| # was passed to init() or for runtimes without a prebuilt arena. | ||
| if device_ids and self._prewarm_config is not None: | ||
| for worker_id in range(len(self._chip_shms)): | ||
| dw.control_prewarm_config(worker_id, self._prewarm_config) | ||
|
|
||
| self._hierarchical_started = True | ||
| with self._hierarchical_start_cv: | ||
| self._hierarchical_start_state = "started" | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -563,6 +563,17 @@ void LocalMailboxEndpoint::control_prepare(const uint8_t *digest) { | |||||||||||||||||||||||||||||||||||||
| run_control_command("control_prepare"); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void LocalMailboxEndpoint::control_prewarm_config(const void *config, size_t config_size) { | ||||||||||||||||||||||||||||||||||||||
| std::lock_guard<std::mutex> lk(mailbox_mu_); | ||||||||||||||||||||||||||||||||||||||
| write_control_args(mbox(), CTRL_PREWARM_CONFIG); | ||||||||||||||||||||||||||||||||||||||
| // Stage the packed CallConfig at MAILBOX_OFF_CONFIG (same slot a task dispatch | ||||||||||||||||||||||||||||||||||||||
| // uses) so the child reads it with its normal config decode. Clamp to the | ||||||||||||||||||||||||||||||||||||||
| // CallConfig region so a mis-sized caller cannot overrun the args area. | ||||||||||||||||||||||||||||||||||||||
| size_t n = config_size < sizeof(CallConfig) ? config_size : sizeof(CallConfig); | ||||||||||||||||||||||||||||||||||||||
| std::memcpy(mbox() + MAILBOX_OFF_CONFIG, config, n); | ||||||||||||||||||||||||||||||||||||||
| run_control_command("control_prewarm_config"); | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+566
to
+574
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Reject partial Line 572 permits undersized input, leaving the mailbox config tail from a prior dispatch while the child decodes a full Proposed fix void LocalMailboxEndpoint::control_prewarm_config(const void *config, size_t config_size) {
std::lock_guard<std::mutex> lk(mailbox_mu_);
+ if (config == nullptr || config_size != sizeof(CallConfig)) {
+ throw std::invalid_argument("control_prewarm_config requires a complete CallConfig");
+ }
write_control_args(mbox(), CTRL_PREWARM_CONFIG);
- size_t n = config_size < sizeof(CallConfig) ? config_size : sizeof(CallConfig);
- std::memcpy(mbox() + MAILBOX_OFF_CONFIG, config, n);
+ std::memcpy(mbox() + MAILBOX_OFF_CONFIG, config, sizeof(CallConfig));
run_control_command("control_prewarm_config");
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+566
to
+575
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If void LocalMailboxEndpoint::control_prewarm_config(const void *config, size_t config_size) {
if (config == nullptr) {
throw std::invalid_argument("control_prewarm_config: config pointer is null");
}
std::lock_guard<std::mutex> lk(mailbox_mu_);
write_control_args(mbox(), CTRL_PREWARM_CONFIG);
// Stage the packed CallConfig at MAILBOX_OFF_CONFIG (same slot a task dispatch
// uses) so the child reads it with its normal config decode. Clamp to the
// CallConfig region so a mis-sized caller cannot overrun the args area.
size_t n = config_size < sizeof(CallConfig) ? config_size : sizeof(CallConfig);
std::memset(mbox() + MAILBOX_OFF_CONFIG, 0, sizeof(CallConfig));
std::memcpy(mbox() + MAILBOX_OFF_CONFIG, config, n);
run_control_command("control_prewarm_config");
}References
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void LocalMailboxEndpoint::control_register(const char *shm_name, size_t blob_size, const uint8_t *digest) { | ||||||||||||||||||||||||||||||||||||||
| std::lock_guard<std::mutex> lk(mailbox_mu_); | ||||||||||||||||||||||||||||||||||||||
| // OFF_ERROR / OFF_ERROR_MSG are cleared by run_control_command — no | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -749,6 +760,11 @@ void WorkerThread::control_prepare(const uint8_t *digest) { | |||||||||||||||||||||||||||||||||||||
| endpoint_->control_prepare(digest); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void WorkerThread::control_prewarm_config(const void *config, size_t config_size) { | ||||||||||||||||||||||||||||||||||||||
| if (!endpoint_) throw std::runtime_error("control_prewarm_config: null endpoint"); | ||||||||||||||||||||||||||||||||||||||
| endpoint_->control_prewarm_config(config, config_size); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void WorkerThread::control_register(const char *shm_name, size_t blob_size, const uint8_t *digest) { | ||||||||||||||||||||||||||||||||||||||
| if (!endpoint_) throw std::runtime_error("control_register: null endpoint"); | ||||||||||||||||||||||||||||||||||||||
| endpoint_->control_register(shm_name, blob_size, digest); | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -977,6 +993,14 @@ void WorkerManager::control_prepare(int worker_id, const uint8_t *digest) { | |||||||||||||||||||||||||||||||||||||
| wt->control_prepare(digest); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void WorkerManager::control_prewarm_config(int worker_id, const void *config, size_t config_size) { | ||||||||||||||||||||||||||||||||||||||
| auto *wt = get_worker_by_id(WorkerType::NEXT_LEVEL, worker_id); | ||||||||||||||||||||||||||||||||||||||
| if (wt == nullptr) { | ||||||||||||||||||||||||||||||||||||||
| throw std::runtime_error("control_prewarm_config: invalid worker_id " + std::to_string(worker_id)); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| wt->control_prewarm_config(config, config_size); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| void WorkerManager::control_alloc_domain(int worker_id, const char *request_shm_name, const char *reply_shm_name) { | ||||||||||||||||||||||||||||||||||||||
| auto *wt = get_worker_by_id(WorkerType::NEXT_LEVEL, worker_id); | ||||||||||||||||||||||||||||||||||||||
| if (wt == nullptr) { | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Fix the invalid ring-sizing kwargs example.
Line 1166 suggests
ring_heap=..., butring_heapbelongs toCallConfig.runtime_env;setattr(config, "ring_heap", ...)will not configure the arena sizing. Document configuringconfig.runtime_env.ring_heapbefore calling this method, or map these kwargs explicitly.🤖 Prompt for AI Agents