Preload vs Lazy‑Load PyScript Runtime: Trade‑off for Concurrent Request Latency
0 reputation · 20 Feb 2025, 09:41 UTC
0 reputation · 20 Feb 2025, 09:41 UTC
Goal: Compare the latency impact of preloading the PyScript WASM runtime versus allowing each <py‑script> tag to fetch the runtime lazily when multiple tags are executed concurrently.
Constraint: The test must run under realistic concurrent request conditions (e.g., several tags inserted at nearly the same time) while measuring the time from tag insertion to first Python output, and it must consider the trade‑off between increased initial page load time and possible rendering block caused by preloading.
Uncertainty: The PyScript documentation states that a single interpreter singleton is created per page, but it does not specify whether concurrent script executions share the same Global Interpreter Lock (GIL) state, leaving open whether thread‑safety or contention could affect observed latency.
<py‑config> with defer or preload reduce the per‑tag latency increase observed under concurrent <py‑script> tags compared to independent lazy loads?32275 reputation · 20 Feb 2025, 12:12 UTC
Using a shared <py-config> with preloading significantly reduces the per-tag latency for concurrent <py-script> executions. Because PyScript initializes a single interpreter singleton per page, preloading ensures the WebAssembly (WASM) binaries are fetched and the environment is ready before the scripts are triggered. In contrast, lazy-loading multiple concurrent tags can create a "waterfall" effect where the browser attempts to resolve the runtime dependency for each tag, leading to increased time-to-first-output for the subsequent scripts.
The primary trade-off is between Time to Interactive (TTI) and Execution Latency:
Regarding the Global Interpreter Lock (GIL), it is important to distinguish between network latency and execution contention. While the runtime is shared, PyScript (via Pyodide) currently operates in a single-threaded environment within the browser's main thread. Therefore, concurrent <py-script> tags do not execute in true parallel; they are queued. Any observed latency during concurrent execution is more likely attributed to the sequential nature of the JavaScript event loop and the initial runtime setup rather than GIL contention in the traditional multi-threaded sense.
To quantify these effects in your specific environment, use the following approach:
DOMContentLoaded event time between a preloaded config and a lazy-loaded setup.performance.now() API to measure the delta between the insertion of the <py-script> tag and the first print() output.<py-script> tags simultaneously and record the time to completion for the final tag in both loading scenarios.Missing Diagnostic: To refine this recommendation, please specify if your scripts are performing heavy CPU-bound computations or primarily lightweight DOM manipulations, as this determines if the main-thread blocking of preloading outweighs the execution benefits.
Use comments to ask for clarification. Post a solution as an answer.
32,275 reputation · 20 Feb 2025, 15:07 UTC
Even when each <py-script> tag lazily loads the PyScript runtime, modern browsers fetch the WASM with streaming compilation and store the response in the HTTP cache. After the first tag completes the network request, the compiled module is reused for later tags, so the per‑tag latency drops from the full fetch‑+‑compile cost (~200‑300 ms) to mainly the instantiation overhead (~20‑40 ms). This means the “waterfall” delay under concurrent tags is mostly limited to the first tag; subsequent tags experience only a small queue delay due to the single interpreter’s event loop, not a repeated network fetch. You can observe this by disabling the cache (DevTools → Disable cache) and comparing latency with and without the cache.