Files
d4r/tools/microcuda/zluda_precompile.py
ayo a627439025 Add MicroCUDA backend, adaptive output wait, wider native K coverage (improve perf)
Shim
- Adaptive output-event wait: sleep to ~120 us before the predicted
  completion (smoothed per wait site), spin near it, back off when late.
  D4R_SHIM_OUTPUT_POLL_US restores a fixed interval.

- Defer the input-marker wait into the worker thread with VRAM interop
  (D4R_SHIM_DEFER_MARKER, default on), taking the host setup off the
  critical path.

MicroCUDA
- Experimental ZLUDA-free backend in wine_nvcuda_bridge, plus
  tools/microcuda and scripts/build_microcuda.sh. Serves accuracy, fast
  and native kernels; unregistered modules fall back to the reference
  path. Unclassified writes invalidate prepared weights.

M kernels
- SWIN_PREP_IDENTITY_AT: key weight prep by allocation identity
  (MicroCUDA); enc3 tube uses it with 8 slots. ZLUDA ignores it.

- dec2 (gfx1101): SWIN_PACKED_RESIDUAL, byte-identical, ~1.6% faster.

- Opt-in gfx12 hardware fp8 conversion (SWIN_FP8_HWCVT), unverified
  on hardware.

Native K translators
- ctile: handle variants that re-materialize the parameter base (mvlo).

- rewrite_output: check live-in registers, not just outputs.

- build.sh: native input covers all 8 flag variants, output the 4 HDR
  "max" variants; depth-tile skip stays mvhi-only.

Tools
- dump_runner and bench_dlss_kernel harness additions.
2026-10-07 15:54:38 -04:00

96 lines
4.4 KiB
Python

#!/usr/bin/env python3
"""Compile every DLSS fatbin with one ZLUDA build into a cache, for make_pack.py.
LD_LIBRARY_PATH=<ROCm lib dir> zluda_precompile.py DLL ZLUDA_LIBCUDA CACHE_HOME SWITCHES [--jobs N]
SWITCHES is the D4R_ZLUDA_ code-generation set the pack is for, as make_pack.py prints it, e.g.
'D4R_ZLUDA_IGNORE_DENORMAL=1,D4R_ZLUDA_IMPLICIT_MAX_BLOCK=256,D4R_ZLUDA_WMMA=1,D4R_ZLUDA_WMMA_FP8=1'.
Each fatbin is loaded with cuModuleLoadData in a worker process (ZLUDA keeps the result in
CACHE_HOME/zluda/ComputeCache/zluda2.db) and unloaded again; nothing is launched. Already cached
modules return at once, so an interrupted run can be resumed. Fatbins without PTX fail (ZLUDA has
nothing to compile) and are left out of the pack. Compiling is CPU heavy and slow for
the big network modules (a minute or more each): do not run it next to a benchmark.
"""
import argparse
import concurrent.futures
import ctypes as c
import os
from pathlib import Path
import sys
import time
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / 'kernels/tools'))
from extract_dlss_ptx import fatbins
def compile_range(libcuda, dll, indices):
lib = c.CDLL(libcuda)
for name, args in (('cuInit', [c.c_uint]), ('cuDeviceGet', [c.POINTER(c.c_int), c.c_int]),
('cuCtxCreate_v2', [c.POINTER(c.c_void_p), c.c_uint, c.c_int]),
('cuModuleLoadData', [c.POINTER(c.c_void_p), c.c_void_p]),
('cuModuleUnload', [c.c_void_p])):
getattr(lib, name).argtypes = args
getattr(lib, name).restype = c.c_int
assert lib.cuInit(0) == 0
device, context = c.c_int(), c.c_void_p()
assert lib.cuDeviceGet(c.byref(device), 0) == 0
assert lib.cuCtxCreate_v2(c.byref(context), 0, device.value) == 0
images = list(fatbins(Path(dll).read_bytes()))
results = []
for i in indices:
image = c.create_string_buffer(images[i][1])
module = c.c_void_p()
start = time.time()
r = lib.cuModuleLoadData(c.byref(module), image)
if r == 0:
lib.cuModuleUnload(module)
results.append((i, r, time.time() - start))
return results
def main():
p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
p.add_argument('dll', type=Path)
p.add_argument('libcuda', type=Path)
p.add_argument('cache_home', type=Path)
p.add_argument('switches')
# ZLUDA's cache is one SQLite file: concurrent writers lose inserts silently (SQLITE_BUSY), so default to 1
p.add_argument('--jobs', type=int, default=1)
# one process per fatbin: a module the compiler aborts on (e.g. WMMA under D4R_ZLUDA_WAVE64=1) is skipped
# instead of ending the run
p.add_argument('--isolate', action='store_true')
a = p.parse_args()
for key in [k for k in os.environ if k.startswith('D4R_ZLUDA_')]:
del os.environ[key]
for item in filter(None, a.switches.split(',')):
key, value = item.split('=', 1)
os.environ[key] = value
a.cache_home.mkdir(parents=True, exist_ok=True)
os.environ['XDG_CACHE_HOME'] = str(a.cache_home.resolve())
count = len(list(fatbins(a.dll.read_bytes())))
# interleave so every worker gets a mix of small and large modules
chunks = [list(range(j, count, a.jobs)) for j in range(a.jobs)]
failed = 0
if a.isolate:
for i in range(count):
with concurrent.futures.ProcessPoolExecutor(1) as pool:
try:
[(_, r, seconds)] = pool.submit(compile_range, str(a.libcuda), str(a.dll), [i]).result()
except concurrent.futures.process.BrokenProcessPool:
r, seconds = 'aborted', 0.0
failed += r != 0
print(f'fatbin {i:3}: {"ok" if r == 0 else f"CUDA error {r}" if r != "aborted" else "compiler aborted"}'
f' ({seconds:.1f} s)', flush=True)
else:
with concurrent.futures.ProcessPoolExecutor(a.jobs) as pool:
for future in concurrent.futures.as_completed(
[pool.submit(compile_range, str(a.libcuda), str(a.dll), chunk) for chunk in chunks]):
for i, r, seconds in future.result():
failed += r != 0
print(f'fatbin {i:3}: {"ok" if r == 0 else f"CUDA error {r}"} ({seconds:.1f} s)', flush=True)
print(f'{count - failed}/{count} fatbins compiled into {a.cache_home}/zluda/ComputeCache/zluda2.db')
if __name__ == '__main__':
main()