Modified fork of NLE 0.9.0 / NetHack 3.6.6 for use with PufferLib's native C vecenv.
Stock NLE uses process-global state (NEARDATA globals, static locals, shared DLB file descriptors), which limits it to one env per process. Multi-env training requires either subprocess isolation or dlopen-per-copy — both slow and memory-heavy.
This fork migrates all mutable state into a per-env nle_ctx_t struct (~75KB), enabling thousands of independent NetHack instances in a single process with zero mutexes:
- Global-to-local migration: All NEARDATA/static globals →
nle_ctx_tfields accessed viacurrent_nle_ctxpointer (initial-exec TLS) - Function-local statics: Per-file state structs for 15+ files with persistent locals (RNG, trap handling, display, save/restore)
- Thread-safe DLB:
pread()replaceslseek()+read()for concurrent data-file access - Per-env arena allocator: Bump allocator for level data, eliminating malloc contention
- Direct linkage: Single shared libnethack.so, no dlopen overhead
The C API (nle_start, nle_step, nle_end) is unchanged. Callers set current_nle_ctx before each call to select which env instance to operate on.
make -C src/build nethack -j8Produces src/build/libnethack.so and src/build/dat/ (game data files).
This repo is cloned automatically by PufferLib's build.sh when building the nethack environment. You don't need to clone it manually.
# In PufferLib:
bash build.sh nethack # clones this repo into vendor/nle/, builds libnethack.soWith PufferLib's OMP-parallel vecenv:
| Envs | Threads | Training SPS |
|---|---|---|
| 64 | 1 | 9,200 |
| 4096 | 1 | 31,300 |
| 4096 | 4 | 136,300 |
NetHack is distributed under the NetHack General Public License. NLE additions are under NLE's license.