Skip to content

Commit 86986be

Browse files
authored
Merge pull request #128 from PyAutoLabs/feature/jax-cache-default
feat: JAX persistent compilation cache by default + XLA_FLAGS append fix (#127)
2 parents a3e7673 + e8d5842 commit 86986be

2 files changed

Lines changed: 137 additions & 9 deletions

File tree

autoconf/jax_wrapper.py

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,28 @@
1616
if not xla_env_set:
1717
logger.info(
1818
"""
19-
For fast JAX compile times, the envirment variable XLA_FLAGS must be set to "--xla_disable_hlo_passes=constant_folding",
19+
For fast JAX compile times, the envirment variable XLA_FLAGS must include "--xla_disable_hlo_passes=constant_folding",
2020
which is currently not.
21-
22-
In Python, to do this manually, use the code:
23-
21+
22+
In Python, to do this manually, use the code:
23+
2424
import os
2525
os.environ["XLA_FLAGS"] = "--xla_disable_hlo_passes=constant_folding"
26-
27-
The environment variable has been set automatically for you now, however if JAX has already been imported,
28-
this change will not take effect and JAX function compiling times may be slow.
29-
26+
27+
The environment variable has been set automatically for you now, however if JAX has already been imported,
28+
this change will not take effect and JAX function compiling times may be slow.
29+
3030
Therefore, it is recommended to set this environment variable before running your script, e.g. in your terminal.
3131
""")
3232

33-
os.environ['XLA_FLAGS'] = "--xla_disable_hlo_passes=constant_folding"
33+
# Append rather than overwrite: replacing the value silently discarded any
34+
# flags the user or a batch script had set (e.g. --xla_dump_to=...,
35+
# --xla_gpu_autotune_level=0), which is indistinguishable from those flags
36+
# having no effect.
37+
if xla_env:
38+
os.environ["XLA_FLAGS"] = f"{xla_env} --xla_disable_hlo_passes=constant_folding"
39+
else:
40+
os.environ["XLA_FLAGS"] = "--xla_disable_hlo_passes=constant_folding"
3441

3542
jax_enable_x64 = os.environ.get("JAX_ENABLE_X64")
3643

@@ -53,6 +60,33 @@
5360
"""
5461
)
5562

63+
if "JAX_COMPILATION_CACHE_DIR" not in os.environ:
64+
65+
_cache_root = os.environ.get("XDG_CACHE_HOME") or os.path.join(
66+
os.path.expanduser("~"), ".cache"
67+
)
68+
_cache_dir = os.path.join(_cache_root, "pyauto_jax")
69+
70+
os.environ["JAX_COMPILATION_CACHE_DIR"] = _cache_dir
71+
72+
logger.info(
73+
f"""
74+
The JAX persistent compilation cache has been enabled at {_cache_dir}
75+
(JAX_COMPILATION_CACHE_DIR). The first fit of a given model and data shape
76+
on this machine compiles its JAX functions, which can take minutes; the
77+
compiled code is cached on disk, so later runs (including after restarting
78+
Python) skip this cost.
79+
80+
To use a different location, set JAX_COMPILATION_CACHE_DIR before running
81+
your script. To disable the cache entirely, set it to an empty string.
82+
"""
83+
)
84+
85+
# An explicitly empty JAX_COMPILATION_CACHE_DIR means "cache disabled"; do not
86+
# force a compile-time threshold in that case.
87+
if os.environ.get("JAX_COMPILATION_CACHE_DIR"):
88+
os.environ.setdefault("JAX_PERSISTENT_CACHE_MIN_COMPILE_TIME_SECS", "1")
89+
5690

5791
def register_pytree_node_class(cls):
5892
"""Opt-in JAX pytree class registration that defers the JAX import.

test_autoconf/test_jax_wrapper.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
"""Tests for the env-var handling in autoconf.jax_wrapper.
2+
3+
The wrapper's env logic runs at import time, so each test reloads the module
4+
under a controlled os.environ. No test imports jax — the wrapper only sets
5+
environment variables.
6+
"""
7+
8+
import importlib
9+
import os
10+
11+
import pytest
12+
13+
import autoconf.jax_wrapper
14+
15+
CONSTANT_FOLDING = "--xla_disable_hlo_passes=constant_folding"
16+
17+
18+
@pytest.fixture
19+
def clean_env(monkeypatch):
20+
for key in (
21+
"XLA_FLAGS",
22+
"JAX_ENABLE_X64",
23+
"JAX_COMPILATION_CACHE_DIR",
24+
"JAX_PERSISTENT_CACHE_MIN_COMPILE_TIME_SECS",
25+
"XDG_CACHE_HOME",
26+
):
27+
monkeypatch.delenv(key, raising=False)
28+
yield monkeypatch
29+
importlib.reload(autoconf.jax_wrapper)
30+
31+
32+
def reload_wrapper():
33+
return importlib.reload(autoconf.jax_wrapper)
34+
35+
36+
def test_xla_flags_set_when_unset(clean_env):
37+
reload_wrapper()
38+
assert os.environ["XLA_FLAGS"] == CONSTANT_FOLDING
39+
40+
41+
def test_xla_flags_appended_not_clobbered(clean_env):
42+
clean_env.setenv("XLA_FLAGS", "--xla_dump_to=/tmp/foo --xla_gpu_autotune_level=0")
43+
reload_wrapper()
44+
flags = os.environ["XLA_FLAGS"]
45+
assert "--xla_dump_to=/tmp/foo" in flags
46+
assert "--xla_gpu_autotune_level=0" in flags
47+
assert CONSTANT_FOLDING in flags
48+
49+
50+
def test_xla_flags_unchanged_when_already_present(clean_env):
51+
preset = f"--xla_dump_to=/tmp/foo {CONSTANT_FOLDING}"
52+
clean_env.setenv("XLA_FLAGS", preset)
53+
reload_wrapper()
54+
assert os.environ["XLA_FLAGS"] == preset
55+
56+
57+
def test_cache_dir_defaulted_when_unset(clean_env):
58+
reload_wrapper()
59+
expected = os.path.join(os.path.expanduser("~"), ".cache", "pyauto_jax")
60+
assert os.environ["JAX_COMPILATION_CACHE_DIR"] == expected
61+
assert os.environ["JAX_PERSISTENT_CACHE_MIN_COMPILE_TIME_SECS"] == "1"
62+
63+
64+
def test_cache_dir_respects_xdg_cache_home(clean_env):
65+
clean_env.setenv("XDG_CACHE_HOME", "/custom/cache")
66+
reload_wrapper()
67+
assert os.environ["JAX_COMPILATION_CACHE_DIR"] == os.path.join(
68+
"/custom/cache", "pyauto_jax"
69+
)
70+
71+
72+
def test_cache_dir_respects_preset_value(clean_env):
73+
clean_env.setenv("JAX_COMPILATION_CACHE_DIR", "/my/cache")
74+
reload_wrapper()
75+
assert os.environ["JAX_COMPILATION_CACHE_DIR"] == "/my/cache"
76+
assert os.environ["JAX_PERSISTENT_CACHE_MIN_COMPILE_TIME_SECS"] == "1"
77+
78+
79+
def test_cache_disabled_by_empty_string(clean_env):
80+
clean_env.setenv("JAX_COMPILATION_CACHE_DIR", "")
81+
reload_wrapper()
82+
assert os.environ["JAX_COMPILATION_CACHE_DIR"] == ""
83+
assert "JAX_PERSISTENT_CACHE_MIN_COMPILE_TIME_SECS" not in os.environ
84+
85+
86+
def test_min_compile_time_respects_preset_value(clean_env):
87+
clean_env.setenv("JAX_PERSISTENT_CACHE_MIN_COMPILE_TIME_SECS", "10")
88+
reload_wrapper()
89+
assert os.environ["JAX_PERSISTENT_CACHE_MIN_COMPILE_TIME_SECS"] == "10"
90+
91+
92+
def test_x64_enabled_by_default(clean_env):
93+
reload_wrapper()
94+
assert os.environ["JAX_ENABLE_X64"] == "True"

0 commit comments

Comments
 (0)