Skip to content

WASM: defer eager Numba gufunc/jit compilation to first use (#930) - #943

Open
kp992 wants to merge 1 commit into
QuantEcon:mainfrom
kp992:wasm-930-lazy-numba
Open

WASM: defer eager Numba gufunc/jit compilation to first use (#930)#943
kp992 wants to merge 1 commit into
QuantEcon:mainfrom
kp992:wasm-930-lazy-numba

Conversation

@kp992

@kp992 kp992 commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Closes #930, part of #925.

What

Four guvectorize callables compiled at import quantecon time are now compiled lazily on first call:

Callable File Change
_probvec_parallel random/utilities.py module-level gufunc → lazy Python wrapper
_probvec_cpu random/utilities.py same
_sample_without_replacement random/utilities.py @guvectorize decorator → lazy wrapper
_ints_arr_to_bits game_theory/vertex_enumeration.py same

How

Gufuncs are replaced by thin Python wrapper functions that create the underlying ufunc on first call and cache it in a module-level sentinel. _probvec_cpu keeps the same name so the game_theory/random.py import is unchanged.

Why this matters for WASM

On native CPython, eager compilation costs milliseconds. In a JupyterLite/WASM kernel, each compile is a full LLVM optimise + WASM object emit + in-process LLD link. The emscripten-forge Numba patches also force-disable cache=True for @guvectorize, so the four gufunc compiles were paid on every session on the critical import path of every notebook.

Public API

No change. qe.random.probvec(..., parallel=...), qe.random.sample_without_replacement(...), and qe.game_theory.vertex_enumeration(...) are unaffected.

@coveralls

coveralls commented Aug 21, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 90.786% (+0.006%) from 90.78% — kp992:wasm-930-lazy-numba into QuantEcon:main

@mmcky

mmcky commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Thanks @kp992. Now that #938 is merged, could you rebase this onto main and drop 97518e0 (the int64 widening from #942)? #929 concluded the intp typing in comb_jit is intentional since its result is an array size, so please keep intp and drop the np.int64 casts, INT64_MAX, the MAX_INT64/test_max_int64 renames and test_simplex_grid_comb_int64 — the lazy @jit(nopython=True, cache=True) over the unchanged body is all #930 needs. The "Depends on #942" line can go too.

Two more things. #930 asks for a before/after warm-cache import quantecon timing in JupyterLite before this merges — could you add it to the PR description, or say so if you can't and we'll decide whether to waive it. And this doesn't address #944 (the upstream reproducer in emscripten-forge/recipes#6309 uses lazy @njit(cache=True) functions only), so please don't reference it as such.

@kp992
kp992 force-pushed the wasm-930-lazy-numba branch from 7ae4be8 to 8a7da64 Compare August 22, 2026 00:07
Four guvectorize callables compiled at import time are now compiled
lazily on first call: _probvec_parallel, _probvec_cpu,
_sample_without_replacement (_ints_arr_to_bits).

Gufuncs are replaced by thin Python wrapper functions that create the
underlying ufunc on first invocation and cache it in a module-level
sentinel. _probvec_cpu keeps the same name so the game_theory/random.py
import is unchanged.

Fixes QuantEcon#930, part of QuantEcon#925.
@kp992
kp992 force-pushed the wasm-930-lazy-numba branch from 8a7da64 to 97325b6 Compare August 22, 2026 00:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WASM: reduce import-time eager Numba compilation

3 participants