Skip to content

Commit bfb82d8

Browse files
Enable XLA on MacOS and fix XLA on GPUs (#707)
1 parent 644dda7 commit bfb82d8

8 files changed

Lines changed: 104 additions & 43 deletions

File tree

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,17 @@ repos:
3636
- --show-source
3737
- --statistics
3838
- repo: https://github.com/asottile/pyupgrade
39-
rev: v3.21.1
39+
rev: v3.21.2
4040
hooks:
4141
- id: pyupgrade
4242
args: ["--py310-plus"]
4343
- repo: https://github.com/PyCQA/isort
44-
rev: 7.0.0
44+
rev: 9.0.0a3
4545
hooks:
4646
- id: isort
4747
args: ["--profile", "black"]
4848
- repo: https://github.com/python/black
49-
rev: 25.9.0
49+
rev: 26.5.1
5050
hooks:
5151
- id: black
5252
- repo: https://github.com/pycqa/pydocstyle

pyproject.toml

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
requires = [
33
"scikit-build-core>=0.10",
44
"nanobind>=2.5.0",
5-
"jax >= 0.4.31; sys_platform == 'win32' or sys_platform == 'linux'"
5+
"jax >= 0.4.31"
66
]
77
build-backend = "scikit_build_core.build"
88

@@ -50,15 +50,15 @@ vector = [
5050
xla = [
5151
"gymnasium>=1.1.0",
5252
"opencv-python>=3.0",
53-
"jax >= 0.4.31; sys_platform == 'win32' or sys_platform == 'linux'",
54-
"chex; sys_platform == 'win32' or sys_platform == 'linux'",
53+
"jax >= 0.4.31",
54+
"chex",
5555
]
5656
test = [
5757
"pytest>=7.0",
5858
"gymnasium>=1.1.0",
5959
"opencv-python>=3.0",
60-
"jax >= 0.4.31; sys_platform == 'win32' or sys_platform == 'linux'",
61-
"chex; sys_platform == 'win32' or sys_platform == 'linux'"
60+
"jax >= 0.4.31",
61+
"chex"
6262
]
6363

6464
[project.urls]
@@ -73,29 +73,26 @@ testpaths = ["tests"]
7373
norecursedirs = ["vcpkg", "build"]
7474

7575
[tool.scikit-build]
76+
# XLA is enabled by default which requires jax to be installed
7677
cmake.args = [
7778
"-DSDL_SUPPORT=ON",
7879
"-DSDL_DYNLOAD=ON",
7980
"-DBUILD_CPP_LIB=OFF",
8081
"-DBUILD_PYTHON_LIB=ON",
81-
"-DBUILD_VECTOR_LIB=ON"
82+
"-DBUILD_VECTOR_LIB=ON",
83+
"-DBUILD_VECTOR_XLA_LIB=ON"
8284
]
8385
cmake.build-type = "Release"
8486
metadata.version.provider = "scikit_build_core.metadata.regex"
8587
metadata.version.input = "version.txt"
8688
metadata.version.regex = "(?P<value>.+)"
8789

88-
# Enable XLA on Windows and Linux
90+
# On windows, vcpkg's default triplet (x64-windows) links dynamically but SDL2
91+
# needs to be static, therefore, force the custom triplet. Linux/macOS
92+
# are unaffected because vcpkg defaults to static linkage there.
8993
[[tool.scikit-build.overrides]]
90-
if.platform-system = "^(win32|linux)"
91-
cmake.args = [
92-
"-DSDL_SUPPORT=ON",
93-
"-DSDL_DYNLOAD=ON",
94-
"-DBUILD_CPP_LIB=OFF",
95-
"-DBUILD_PYTHON_LIB=ON",
96-
"-DBUILD_VECTOR_LIB=ON",
97-
"-DBUILD_VECTOR_XLA_LIB=ON" # add vector-xla-lib="ON"
98-
]
94+
if.platform-system = "^win32"
95+
cmake.define.VCPKG_TARGET_TRIPLET = "x64-windows-mixed"
9996

10097
[tool.cibuildwheel]
10198
# Skip 32-bit wheels, PyPy, musllinux, cp313t (free-threading was experimental in 3.13),

scripts/benchmark-vec-env.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
44
Allows testing of the impact of `num_envs`, `batch_size`, `autoreset_mode`, and `num_threads` on steps per second and resource usage.
55
"""
6+
67
from __future__ import annotations
78

89
import dataclasses

src/ale/python/CMakeLists.txt

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,37 @@ if (BUILD_VECTOR_LIB AND BUILD_VECTOR_XLA_LIB)
2222
)
2323
message(STATUS "XLA include directory: '${XLA_DIR}'")
2424

25-
# Find CUDA toolkit for headers and linking (optional - XLA disabled if not found)
25+
# CUDA toolkit is optional: when found we additionally compile/link the GPU
26+
# handlers, otherwise XLA is built with CPU-only support (e.g. macOS).
2627
find_package(CUDAToolkit QUIET)
2728

29+
# Always build the CPU XLA handlers. The GPU handlers in
30+
# ale_vector_xla_interface.cpp are guarded by BUILD_VECTOR_XLA_GPU, which is
31+
# only defined below when the CUDA Toolkit is found, so they are naturally
32+
# skipped when CUDA is unavailable (e.g. macOS).
33+
target_sources(_ale_py PRIVATE ale_vector_python_interface.cpp ale_vector_xla_interface.cpp)
34+
target_include_directories(_ale_py PUBLIC ${XLA_DIR})
35+
target_compile_definitions(_ale_py PRIVATE BUILD_VECTOR_LIB BUILD_VECTOR_XLA_LIB)
36+
set_target_properties(_ale_py PROPERTIES
37+
POSITION_INDEPENDENT_CODE ON
38+
)
39+
2840
if (CUDAToolkit_FOUND)
29-
message(STATUS "CUDA Toolkit found: ${CUDAToolkit_VERSION}")
30-
target_sources(_ale_py PRIVATE ale_vector_python_interface.cpp ale_vector_xla_interface.cpp)
31-
target_include_directories(_ale_py PUBLIC ${XLA_DIR})
41+
message(STATUS "CUDA Toolkit found: ${CUDAToolkit_VERSION} - enabling GPU XLA support")
42+
# The GPU handlers only use the CUDA runtime API (no device kernels), so
43+
# the host compiler can build them given the CUDA headers and cudart.
3244
target_include_directories(_ale_py PRIVATE ${CUDAToolkit_INCLUDE_DIRS})
33-
target_compile_definitions(_ale_py PRIVATE BUILD_VECTOR_LIB BUILD_VECTOR_XLA_LIB)
34-
target_link_libraries(_ale_py PRIVATE CUDA::cudart)
35-
36-
set_target_properties(_ale_py PROPERTIES
37-
POSITION_INDEPENDENT_CODE ON
38-
)
45+
target_compile_definitions(_ale_py PRIVATE BUILD_VECTOR_XLA_GPU)
46+
if (WIN32)
47+
# Static linking bakes cudart into _ale_py.pyd so there is no runtime DLL to find.
48+
target_link_libraries(_ale_py PRIVATE CUDA::cudart_static)
49+
else()
50+
# The loader resolves libcudart via the toolkit's lib path / the manylinux image, and
51+
# jax already loads the same runtime in-process, so there is no missing-library issue.
52+
target_link_libraries(_ale_py PRIVATE CUDA::cudart)
53+
endif()
3954
else()
40-
message(WARNING "CUDA Toolkit not found - disabling XLA support. Install CUDA Toolkit to enable XLA.")
41-
target_sources(_ale_py PRIVATE ale_vector_python_interface.cpp)
42-
target_compile_definitions(_ale_py PRIVATE BUILD_VECTOR_LIB)
55+
message(STATUS "CUDA Toolkit not found - building CPU-only XLA support")
4356
endif()
4457
elseif (BUILD_VECTOR_LIB)
4558
target_sources(_ale_py PRIVATE ale_vector_python_interface.cpp)

src/ale/python/__init__.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@
1717
ctypes.CDLL("vcruntime140.dll")
1818
ctypes.CDLL("msvcp140.dll")
1919
except OSError:
20-
raise OSError(
21-
"""Microsoft Visual C++ Redistribution Pack is not installed.
22-
It can be downloaded from https://aka.ms/vs/16/release/vc_redist.x64.exe."""
23-
)
20+
raise OSError("""Microsoft Visual C++ Redistribution Pack is not installed.
21+
It can be downloaded from https://aka.ms/vs/16/release/vc_redist.x64.exe.""")
2422

2523
# Loading DLLs on Windows is kind of a disaster
2624
# The best approach seems to be using LoadLibraryEx with user defined search paths.

src/ale/python/ale_vector_xla_interface.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#include <cstring> // For memcpy
1010
#include <iostream>
1111

12-
#ifdef __CUDACC__
12+
#ifdef BUILD_VECTOR_XLA_GPU
1313
#include <cuda_runtime.h>
1414
#endif
1515

@@ -108,7 +108,7 @@ XLA_FFI_DEFINE_HANDLER_SYMBOL(
108108
.Ret<ffi::Buffer<ffi::S32>>() // episode_frame_numbers
109109
);
110110

111-
#ifdef __CUDACC__
111+
#ifdef BUILD_VECTOR_XLA_GPU
112112
// GPU version of XLAReset with CUDA stream support
113113
ffi::Error XLAResetGPUImpl(
114114
cudaStream_t stream,
@@ -270,7 +270,7 @@ XLA_FFI_DEFINE_HANDLER_SYMBOL(
270270
.Ret<ffi::Buffer<ffi::S32>>() // frame_numbers
271271
.Ret<ffi::Buffer<ffi::S32>>() // episode_frame_numbers
272272
);
273-
#endif // __CUDACC__
273+
#endif // BUILD_VECTOR_XLA_GPU
274274

275275
// CPU version of XLAStep
276276
ffi::Error XLAStepImpl(
@@ -394,7 +394,7 @@ XLA_FFI_DEFINE_HANDLER_SYMBOL(
394394
.Ret<ffi::Buffer<ffi::S32>>() // episode_frame_numbers
395395
);
396396

397-
#ifdef __CUDACC__
397+
#ifdef BUILD_VECTOR_XLA_GPU
398398
// GPU version of XLAStep with CUDA stream support
399399
ffi::Error XLAStepGPUImpl(
400400
cudaStream_t stream,
@@ -610,7 +610,7 @@ XLA_FFI_DEFINE_HANDLER_SYMBOL(
610610
.Ret<ffi::Buffer<ffi::S32>>() // frame_numbers
611611
.Ret<ffi::Buffer<ffi::S32>>() // episode_frame_numbers
612612
);
613-
#endif // __CUDACC__
613+
#endif // BUILD_VECTOR_XLA_GPU
614614

615615
template <typename T>
616616
nb::capsule EncapsulateFFICall(T *fn) {
@@ -625,7 +625,7 @@ void init_vector_module_xla(nb::module_& m) {
625625
m.def("VectorXLAReset", [] {return EncapsulateFFICall(AtariVectorEnvXLAReset); });
626626
m.def("VectorXLAStep", [] {return EncapsulateFFICall(AtariVectorEnvXLAStep); });
627627

628-
#ifdef __CUDACC__
628+
#ifdef BUILD_VECTOR_XLA_GPU
629629
// GPU handlers
630630
m.def("VectorXLAResetGPU", [] {return EncapsulateFFICall(AtariVectorEnvXLAResetGPU); });
631631
m.def("VectorXLAStepGPU", [] {return EncapsulateFFICall(AtariVectorEnvXLAStepGPU); });

src/ale/python/env.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ def __init__(
189189
low=0, high=255, dtype=np.uint8, shape=(self.ale.getRAMSize(),)
190190
)
191191
elif self._obs_type == "rgb" or self._obs_type == "grayscale":
192-
(screen_height, screen_width) = self.ale.getScreenDims()
192+
screen_height, screen_width = self.ale.getScreenDims()
193193
image_shape = (
194194
screen_height,
195195
screen_width,

tests/python/test_atari_vector_xla.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,55 @@ def actor(handle, action):
130130

131131
envs_1.close()
132132
envs_2.close()
133+
134+
135+
CPU_DEVICES = jax.devices("cpu")
136+
try:
137+
GPU_DEVICES = jax.devices("gpu")
138+
except RuntimeError:
139+
GPU_DEVICES = []
140+
141+
142+
def is_gpu_array(arr):
143+
return all(arr_device in GPU_DEVICES for arr_device in arr.devices())
144+
145+
146+
def is_cpu_array(arr):
147+
return all(arr_device in CPU_DEVICES for arr_device in arr.devices())
148+
149+
150+
def test_xla_dispatch_gpu():
151+
envs = AtariVectorEnv("pong", num_envs=1)
152+
handle, xla_reset, xla_step = envs.xla()
153+
154+
print(f"GPU_DEVICES={GPU_DEVICES}, CPU_DEVICES={CPU_DEVICES}")
155+
if GPU_DEVICES:
156+
print("RUNNING ON GPUs")
157+
handle, (obs, info) = xla_reset(handle)
158+
assert is_gpu_array(obs)
159+
assert is_gpu_array(info["frame_number"])
160+
161+
actions = np.zeros(1, dtype=np.int32)
162+
handle, (obs, rewards, terminations, truncations, info) = xla_step(
163+
handle, actions
164+
)
165+
assert is_gpu_array(obs)
166+
assert is_gpu_array(rewards)
167+
assert is_gpu_array(terminations)
168+
assert is_gpu_array(truncations)
169+
assert is_gpu_array(info["frame_number"])
170+
else:
171+
print("RUNNING ON CPUs")
172+
handle, (obs, info) = xla_reset(handle)
173+
assert is_cpu_array(obs)
174+
assert is_cpu_array(info["frame_number"])
175+
176+
actions = np.zeros(1, dtype=np.int32)
177+
handle, (obs, rewards, terminations, truncations, info) = xla_step(
178+
handle, actions
179+
)
180+
assert is_cpu_array(obs)
181+
assert is_cpu_array(rewards)
182+
assert is_cpu_array(terminations)
183+
assert is_cpu_array(truncations)
184+
assert is_cpu_array(info["frame_number"])

0 commit comments

Comments
 (0)