Skip to content

Commit 2ecda9a

Browse files
authored
[envpool] stabilize manylinux release wheel builds (#339)
###### Why/Context/Summary - Fix the new `manylinux_2_28_x86_64` release path so the `Release PyPI Wheel` matrix can build wheels for Python `3.11`, `3.12`, and `3.13` again. - The failure surface has moved several times while exercising the real release lane, so this PR now carries the minimal release-only fixes needed to keep the matrix moving forward: - remove `typed-ast` from `make auditwheel-install` for CPython `3.13` - install the Perl `Compress::Zlib` dependency needed by NASM on manylinux - correct NASM feature detection for `stdbit.h` and `strlcpy` - provide a CPU-only CUDA stub for release builds that do not have a real CUDA toolkit - force SDL2/OpenCV CMake installs to use `lib/` instead of `lib64/` so `rules_foreign_cc` can find the expected static archives - Keep the changes scoped to the release lane and release-time third-party build glue; no runtime env logic changes are intended here. ###### Test plan - local: `make auditwheel-install` - GitHub Actions: watch PR `Release PyPI Wheel` matrix on every commit (`pull_request` trigger enabled in this branch) - current loop: keep fixing deterministic release blockers until the full `3.11/3.12/3.13` wheel matrix succeeds, then validate the generated wheels on `dev`
1 parent 76df1b0 commit 2ecda9a

29 files changed

Lines changed: 198 additions & 87 deletions

.github/workflows/release.yml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
name: Release PyPI Wheel
22

3-
# on: [push, pull_request]
43
on:
54
push:
65
branches:
@@ -13,6 +12,7 @@ jobs:
1312
runs-on: ubuntu-latest
1413
container: quay.io/pypa/manylinux_2_28_x86_64
1514
strategy:
15+
fail-fast: false
1616
matrix:
1717
python-version: ["3.11", "3.12", "3.13"]
1818
steps:
@@ -44,21 +44,24 @@ jobs:
4444
run: |
4545
make release-test
4646
- name: Upload artifact
47-
uses: actions/upload-artifact@main
47+
uses: actions/upload-artifact@v7
4848
with:
49-
name: wheel
49+
name: wheel-${{ matrix.python-version }}
5050
path: wheelhouse/
51+
if-no-files-found: error
5152

5253
publish:
5354
runs-on: ubuntu-latest
5455
needs: [release]
5556
steps:
56-
- uses: actions/download-artifact@v3
57+
- uses: actions/download-artifact@v8
5758
with:
59+
pattern: wheel-*
5860
path: artifact
61+
merge-multiple: true
5962
- name: Move files so the next action can find them
6063
run: |
61-
mkdir dist && mv artifact/wheel/* dist/
64+
mkdir dist && mv artifact/* dist/
6265
ls dist/
6366
- name: Publish distribution to PyPI
6467
if: startsWith(github.ref, 'refs/tags')

BUILD

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,58 @@
1+
load("@python_versions//3.11:defs.bzl", py_binary_311 = "py_binary")
2+
load("@python_versions//3.12:defs.bzl", py_binary_312 = "py_binary")
3+
load("@python_versions//3.13:defs.bzl", py_binary_313 = "py_binary")
14
load("@rules_python//python:defs.bzl", "py_binary")
25
load("//envpool:requirements.bzl", "requirement")
36

7+
_SETUP_SRCS = [
8+
"setup.py",
9+
]
10+
11+
_SETUP_DATA = [
12+
"README.md",
13+
"setup.cfg",
14+
"//envpool",
15+
]
16+
17+
_SETUP_DEPS = [
18+
requirement("setuptools"),
19+
requirement("wheel"),
20+
]
21+
422
filegroup(
523
name = "clang_tidy_config",
624
data = [".clang-tidy"],
725
)
826

927
py_binary(
1028
name = "setup",
11-
srcs = [
12-
"setup.py",
13-
],
14-
data = [
15-
"README.md",
16-
"setup.cfg",
17-
"//envpool",
18-
],
29+
srcs = _SETUP_SRCS,
30+
data = _SETUP_DATA,
1931
main = "setup.py",
2032
python_version = "PY3",
21-
deps = [
22-
requirement("setuptools"),
23-
requirement("wheel"),
24-
],
33+
deps = _SETUP_DEPS,
34+
)
35+
36+
py_binary_311(
37+
name = "setup_py311",
38+
srcs = _SETUP_SRCS,
39+
data = _SETUP_DATA,
40+
main = "setup.py",
41+
deps = _SETUP_DEPS,
42+
)
43+
44+
py_binary_312(
45+
name = "setup_py312",
46+
srcs = _SETUP_SRCS,
47+
data = _SETUP_DATA,
48+
main = "setup.py",
49+
deps = _SETUP_DEPS,
50+
)
51+
52+
py_binary_313(
53+
name = "setup_py313",
54+
srcs = _SETUP_SRCS,
55+
data = _SETUP_DATA,
56+
main = "setup.py",
57+
deps = _SETUP_DEPS,
2558
)

Makefile

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ BAZEL = USE_BAZEL_VERSION=$(BAZEL_VERSION) $(BAZELISK_BIN)
1313
DATE = $(shell date "+%Y-%m-%d")
1414
DOCKER_TAG = $(DATE)-$(COMMIT_HASH)
1515
DOCKER_USER = trinkle23897
16+
RELEASE_PYTHON ?= $(shell python3 -c 'import sys; print("{}.{}".format(sys.version_info[0], sys.version_info[1]))')
17+
RELEASE_SETUP_TARGET = //:setup_py$(subst .,,$(RELEASE_PYTHON))
1618
CLANG_TIDY_MAJOR = 18
1719
CLANG_TIDY_BIN = clang-tidy-$(CLANG_TIDY_MAJOR)
1820
CLANG_TIDY_WRAPPER_DIR = $(HOME)/.cache/$(PROJECT_NAME)/bin
@@ -78,7 +80,13 @@ spelling-system-install:
7880
python3 -c "import ctypes.util, sys; sys.exit(0 if ctypes.util.find_library('enchant-2') or ctypes.util.find_library('enchant') else 1)")
7981

8082
auditwheel-install:
81-
$(call check_install_extra, auditwheel, auditwheel typed-ast patchelf)
83+
$(call check_install_extra, auditwheel, auditwheel patchelf)
84+
85+
release-system-install:
86+
if command -v dnf >/dev/null 2>&1; then \
87+
perl -MCompress::Zlib -e1 >/dev/null 2>&1 || \
88+
(dnf install -y perl-IO-Compress && dnf clean all); \
89+
fi
8290

8391
# python linter
8492

@@ -125,10 +133,10 @@ bazel-build: bazel-install bazel-pip-requirement-dev
125133
mkdir -p dist
126134
cp bazel-bin/setup.runfiles/$(PROJECT_NAME)/dist/*.whl ./dist
127135

128-
bazel-release: bazel-install bazel-pip-requirement-release
129-
$(BAZEL) run $(BAZELOPT) //:setup --config=release -- bdist_wheel
136+
bazel-release: bazel-install bazel-pip-requirement-release release-system-install
137+
$(BAZEL) run $(BAZELOPT) $(RELEASE_SETUP_TARGET) --config=release -- bdist_wheel
130138
mkdir -p dist
131-
cp bazel-bin/setup.runfiles/$(PROJECT_NAME)/dist/*.whl ./dist
139+
cp bazel-bin/$(subst //:,,$(RELEASE_SETUP_TARGET)).runfiles/$(PROJECT_NAME)/dist/*.whl ./dist
132140

133141
bazel-test: bazel-install bazel-pip-requirement-dev
134142
$(BAZEL) test --test_output=all $(BAZELOPT) //... --config=test --spawn_strategy=local --color=yes
@@ -194,7 +202,9 @@ docker-release-launch: docker-release
194202
docker run --network=host -v /:/host -v $(shell pwd):/app -v $(HOME)/.cache:/root/.cache --shm-size=4gb -it $(PROJECT_NAME)-release:$(DOCKER_TAG) zsh
195203

196204
pypi-wheel: auditwheel-install bazel-release
197-
ls dist/*.whl -Art | tail -n 1 | xargs auditwheel repair --plat manylinux_2_28_x86_64
205+
rm -rf wheelhouse
206+
CURRENT_WHEEL=$$(ls dist/*.whl -Art | tail -n 1); \
207+
python3 -m auditwheel repair --plat manylinux_2_28_x86_64 "$$CURRENT_WHEEL"
198208

199209
release-test1:
200210
cd envpool && python3 make_test.py

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,19 @@ The example scripts are under [examples/](https://github.com/sail-sg/envpool/tre
7272

7373
## Benchmark Results
7474

75-
We perform our benchmarks with ALE Atari environment `PongNoFrameskip-v4` (with environment wrappers from [OpenAI Baselines](https://github.com/openai/baselines/blob/master/baselines/common/atari_wrappers.py)) and Mujoco environment `Ant-v3` on different hardware setups, including a TPUv3-8 virtual machine (VM) of 96 CPU cores and 2 NUMA nodes, and an NVIDIA DGX-A100 of 256 CPU cores with 8 NUMA nodes. Baselines include 1) naive Python for-loop; 2) the most popular RL environment parallelization execution by Python subprocess, e.g., [gym.vector_env](https://github.com/openai/gym/blob/master/gym/vector/vector_env.py); 3) to our knowledge, the fastest RL environment executor [Sample Factory](https://github.com/alex-petrenko/sample-factory) before EnvPool.
75+
The historical benchmark tables below were produced with ALE Atari environment
76+
`PongNoFrameskip-v4` (with environment wrappers from [OpenAI
77+
Baselines](https://github.com/openai/baselines/blob/master/baselines/common/atari_wrappers.py))
78+
and Mujoco environment `Ant-v3` on different hardware setups, including a
79+
TPUv3-8 virtual machine (VM) of 96 CPU cores and 2 NUMA nodes, and an NVIDIA
80+
DGX-A100 of 256 CPU cores with 8 NUMA nodes. The current scripts under
81+
[`benchmark/`](https://github.com/sail-sg/envpool/tree/main/benchmark) use
82+
Gymnasium's `ALE/Pong-v5` and `Ant-v5`. Baselines include 1) naive Python
83+
for-loop; 2) the most popular RL environment parallelization execution by
84+
Python subprocess, e.g.,
85+
[gym.vector_env](https://github.com/openai/gym/blob/master/gym/vector/vector_env.py);
86+
3) to our knowledge, the fastest RL environment executor [Sample
87+
Factory](https://github.com/alex-petrenko/sample-factory) before EnvPool.
7688

7789
We report EnvPool performance with sync mode, async mode, and NUMA + async mode, compared with the baselines on different number of workers (i.e., number of CPU cores). As we can see from the results, EnvPool achieves significant improvements over the baselines on all settings. On the high-end setup, EnvPool achieves 1 Million frames per second with Atari and 3 Million frames per second with Mujoco on 256 CPU cores, which is 14.9x / 19.6x of the `gym.vector_env` baseline. On a typical PC setup with 12 CPU cores, EnvPool's throughput is 3.1x / 2.9x of `gym.vector_env`.
7890

docker/release.dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ WORKDIR $HOME
99

1010
RUN dnf install -y \
1111
git curl wget zsh gcc gcc-c++ make tmux golang java-17-openjdk-devel \
12-
qt5-qtbase-devel qt5-qtdeclarative-devel \
12+
qt5-qtbase-devel qt5-qtdeclarative-devel perl-IO-Compress \
1313
&& dnf clean all
1414
RUN ln -sf "$(qmake-qt5 -query QT_INSTALL_HEADERS)" /usr/include/qt
1515

docs/content/build.rst

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
Build From Source
22
=================
33

4-
We recommend building EnvPool on Ubuntu 22.04 environment.
4+
We recommend developing EnvPool on Ubuntu 24.04. Release wheels are built in a
5+
``manylinux_2_28_x86_64`` environment.
56

67
We use `bazel <https://bazel.build/>`_ to build EnvPool. Comparing with
78
`pip <https://pip.pypa.io/>`_, using Bazel to build python package with C++ .so
@@ -30,7 +31,7 @@ or `golang <https://golang.org/doc/install>`_ with version >= 1.16:
3031

3132
.. code-block:: bash
3233
33-
sudo apt install -y golang
34+
sudo apt install -y golang-go
3435
export PATH=$HOME/go/bin:$PATH
3536
go install github.com/bazelbuild/bazelisk@latest
3637
ln -sf $HOME/go/bin/bazelisk $HOME/go/bin/bazel
@@ -66,25 +67,25 @@ or `golang <https://golang.org/doc/install>`_ with version >= 1.16:
6667
Install Other Dependencies
6768
--------------------------
6869

69-
EnvPool requires **GCC/G++ version >= 9.0** to build the source code. To install:
70+
EnvPool currently builds with the system GCC/G++ toolchain on Ubuntu 24.04. To
71+
install the required development packages:
7072

7173
.. code-block:: bash
7274
73-
# optional
74-
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
75+
sudo apt install -y build-essential python3-dev python3-pip \
76+
python-is-python3 golang-go qtbase5-dev qtdeclarative5-dev
7577
76-
# install
77-
sudo apt install -y gcc-9 g++-9 build-essential
78-
79-
# to change the default cc to gcc-9:
80-
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 60 --slave /usr/bin/g++ g++ /usr/bin/g++-9
78+
# Some Bazel Qt rules still look for this legacy include path.
79+
sudo ln -sf /usr/include/x86_64-linux-gnu/qt5 /usr/include/qt
8180
8281
It also requires **Python version >= 3.11**:
8382

8483
.. code-block:: bash
8584
86-
sudo apt install -y python3-dev python3-pip
87-
sudo ln -sf /usr/bin/python3 /usr/bin/python
85+
python3 --version
86+
87+
The default build and test shortcuts in this repo use **Bazel 8.6.0** via
88+
``bazelisk``.
8889

8990
Install CUDA to enable XLA: see https://developer.nvidia.com/cuda-downloads
9091

docs/env/box2d.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ Notes on Box2D versions
155155
``box2d_correctness_test.py`` aims to address the correctness of our Box2D
156156
implementation.
157157

158-
We ran gym's environments with dependency ``box2d-py==2.3.5`` (see
158+
We used gym's historical baseline with dependency ``box2d-py==2.3.5`` (see
159159
https://github.com/openai/box2d-py/tree/2.3.5) for 1000 episode and averaged
160160
reward as our threshold.
161161

docs/spelling_wordlist.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
threadpool
2+
toolchain
23
async
34
dm
45
env

envpool/mujoco/gym/mujoco_env.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <mjxmacro.h>
2121
#include <mujoco.h>
2222

23+
#include <fstream>
2324
#include <stdexcept>
2425
#include <string>
2526

@@ -28,6 +29,17 @@ namespace mujoco_gym {
2829
class MujocoEnv {
2930
private:
3031
std::array<char, 1000> error_;
32+
std::string xml_path_;
33+
34+
static std::string ResolveXMLPath(const std::string& xml) {
35+
auto ext = xml.rfind(".xml");
36+
if (ext == std::string::npos) {
37+
return xml;
38+
}
39+
std::string patched = xml.substr(0, ext) + "_envpool.xml";
40+
std::ifstream stream(patched);
41+
return stream.good() ? patched : xml;
42+
}
3143

3244
protected:
3345
mjModel* model_;
@@ -44,7 +56,8 @@ class MujocoEnv {
4456
public:
4557
MujocoEnv(const std::string& xml, int frame_skip, bool post_constraint,
4658
int max_episode_steps)
47-
: model_(mj_loadXML(xml.c_str(), nullptr, error_.begin(), 1000)),
59+
: xml_path_(ResolveXMLPath(xml)),
60+
model_(mj_loadXML(xml_path_.c_str(), nullptr, error_.begin(), 1000)),
4861
frame_skip_(frame_skip),
4962
post_constraint_(post_constraint),
5063
max_episode_steps_(max_episode_steps),

envpool/pip.bzl

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,27 @@
1414

1515
"""EnvPool pip requirements initialization, this is loaded in WORKSPACE."""
1616

17-
load("@rules_python//python:pip.bzl", "pip_parse")
17+
load("@python_versions//:pip.bzl", "multi_pip_parse")
1818

1919
def workspace():
2020
"""Configure pip requirements."""
2121

2222
if "pip_requirements" not in native.existing_rules().keys():
23-
pip_parse(
23+
multi_pip_parse(
2424
name = "pip_requirements",
25-
python_interpreter_target = "@python3_12_x86_64-unknown-linux-gnu//:bin/python3",
25+
default_version = "3.12",
26+
python_interpreter_target = {
27+
"3.11": "@python_versions_3_11_x86_64-unknown-linux-gnu//:bin/python3",
28+
"3.12": "@python_versions_3_12_x86_64-unknown-linux-gnu//:bin/python3",
29+
"3.13": "@python_versions_3_13_x86_64-unknown-linux-gnu//:bin/python3",
30+
},
31+
requirements_lock = {
32+
"3.11": "@envpool//third_party/pip_requirements:requirements.txt",
33+
"3.12": "@envpool//third_party/pip_requirements:requirements.txt",
34+
"3.13": "@envpool//third_party/pip_requirements:requirements.txt",
35+
},
2636
# default timeout value is 600, change it if you failed.
2737
# timeout = 3600,
2838
quiet = False,
29-
requirements_lock = "@envpool//third_party/pip_requirements:requirements.txt",
3039
# extra_pip_args = ["--extra-index-url", "https://mirrors.aliyun.com/pypi/simple"],
3140
)

0 commit comments

Comments
 (0)