IQ.Pilot Release Commit @ 461be14
This commit is contained in:
@@ -7,14 +7,20 @@ import argparse
|
||||
import gc
|
||||
import os
|
||||
import pickle
|
||||
import sys
|
||||
import time
|
||||
|
||||
os.environ.setdefault("DEV", "USB+AMD:LLVM")
|
||||
os.environ.setdefault("FLOAT16", "1")
|
||||
os.environ.setdefault("JIT_BATCH_SIZE", "0")
|
||||
os.environ.setdefault("GMMU", "0")
|
||||
os.environ.setdefault("TC_OPT", "2")
|
||||
|
||||
HOST = "--host" in sys.argv
|
||||
if HOST:
|
||||
from iqpilot.selfdrive.iqmodeld.tools.egpu_host_mock import DEFAULT_ARCH, activate
|
||||
activate(sys.argv[sys.argv.index("--arch") + 1] if "--arch" in sys.argv else DEFAULT_ARCH)
|
||||
os.environ.setdefault("DEV", "USB+AMD:LLVM")
|
||||
|
||||
import numpy as np
|
||||
|
||||
from iqpilot.selfdrive.iqmodeld.egpu_helpers import egpu_pkl_path, local_onnx, patch_tinygrad_fetch_fw
|
||||
@@ -150,7 +156,7 @@ def compile_policy_model(meta: dict, onnx_path: str, out_path: str) -> str:
|
||||
from tinygrad.engine.jit import TinyJit
|
||||
from tinygrad.nn.onnx import OnnxRunner
|
||||
|
||||
from iqpilot.selfdrive.iqmodeld.egpu_policy import POLICY_FORMAT, PackedInputs, make_queues, make_run_policy
|
||||
from iqpilot.selfdrive.iqmodeld.egpu_policy import POLICY_FORMAT, PackedInputs, dump_oob, load_bundle, make_queues, make_run_policy
|
||||
|
||||
if meta.get("split"):
|
||||
raise RuntimeError(f"model {meta['key']} is a split model; eGPU compiles fused models only")
|
||||
@@ -176,7 +182,7 @@ def compile_policy_model(meta: dict, onnx_path: str, out_path: str) -> str:
|
||||
baseline = step(SEED + i)
|
||||
if baseline.shape[0] != meta["output_len"]:
|
||||
raise RuntimeError(f"model output length {baseline.shape[0]} != registry {meta['output_len']}")
|
||||
if not np.isfinite(baseline).all():
|
||||
if not HOST and not np.isfinite(baseline).all():
|
||||
raise RuntimeError("compiled policy produced non-finite outputs")
|
||||
|
||||
bundle = {
|
||||
@@ -191,16 +197,15 @@ def compile_policy_model(meta: dict, onnx_path: str, out_path: str) -> str:
|
||||
}
|
||||
os.makedirs(os.path.dirname(out_path), exist_ok=True)
|
||||
tmp = out_path + ".part"
|
||||
print("serialize")
|
||||
print("serialize (out-of-band buffers)")
|
||||
with open(tmp, "wb") as f:
|
||||
pickle.dump(bundle, f, protocol=pickle.HIGHEST_PROTOCOL)
|
||||
dump_oob(bundle, f)
|
||||
|
||||
del bundle, jit, queues, packed
|
||||
gc.collect()
|
||||
|
||||
print("reload + validate")
|
||||
with open(tmp, "rb") as f:
|
||||
jit = pickle.load(f)["run_policy"]
|
||||
jit = load_bundle(tmp)["run_policy"]
|
||||
queues = make_queues(input_spec, frame_skip, device)
|
||||
packed = PackedInputs(input_spec)
|
||||
outs = []
|
||||
@@ -211,6 +216,9 @@ def compile_policy_model(meta: dict, onnx_path: str, out_path: str) -> str:
|
||||
flat = out.numpy().reshape(-1)
|
||||
packed.views["prev_feat"][:] = flat[meta["output_slices"]["hidden_state"]].reshape(packed.views["prev_feat"].shape)
|
||||
outs.append(flat)
|
||||
if HOST:
|
||||
os.replace(tmp, out_path)
|
||||
return out_path
|
||||
if not np.array_equal(outs[-1], baseline):
|
||||
raise RuntimeError("policy outputs differ from baseline after pickle round trip")
|
||||
if np.array_equal(outs[0], outs[-1]):
|
||||
@@ -234,7 +242,11 @@ def main() -> None:
|
||||
p.add_argument("--progress-base", type=float, default=None)
|
||||
p.add_argument("--progress-span", type=float, default=0.0)
|
||||
p.add_argument("--format", type=int, default=2, choices=(1, 2))
|
||||
p.add_argument("--host", action="store_true", help="compile on a mock dock (no AMD hardware); outputs need a dock parity gate")
|
||||
p.add_argument("--arch", default=None, help="target gfx arch for --host")
|
||||
args = p.parse_args()
|
||||
if args.host and args.format != 2:
|
||||
raise SystemExit("--host supports format 2 only")
|
||||
|
||||
if args.model is not None:
|
||||
if args.model in EGPU_MODELS:
|
||||
|
||||
44
iqpilot/selfdrive/iqmodeld/tools/convert_egpu_oob.py
Normal file
44
iqpilot/selfdrive/iqmodeld/tools/convert_egpu_oob.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import gc
|
||||
import os
|
||||
|
||||
os.environ.setdefault("DEV", "USB+AMD:LLVM")
|
||||
os.environ.setdefault("GMMU", "0")
|
||||
|
||||
from iqpilot.selfdrive.iqmodeld.egpu_helpers import patch_tinygrad_fetch_fw
|
||||
from iqpilot.selfdrive.iqmodeld.egpu_policy import dump_oob, is_oob, load_bundle
|
||||
|
||||
|
||||
def convert(src: str, dst: str) -> str:
|
||||
patch_tinygrad_fetch_fw()
|
||||
if is_oob(src):
|
||||
if src != dst:
|
||||
os.replace(src, dst)
|
||||
return dst
|
||||
bundle = load_bundle(src)
|
||||
tmp = dst + ".part"
|
||||
with open(tmp, "wb") as f:
|
||||
dump_oob(bundle, f)
|
||||
del bundle
|
||||
gc.collect()
|
||||
load_bundle(tmp)
|
||||
os.replace(tmp, dst)
|
||||
return dst
|
||||
|
||||
|
||||
def main() -> None:
|
||||
p = argparse.ArgumentParser()
|
||||
p.add_argument("src")
|
||||
p.add_argument("--out", default=None)
|
||||
args = p.parse_args()
|
||||
out = convert(args.src, args.out or args.src)
|
||||
print(f"converted -> {out} ({os.path.getsize(out) / 1e6:.1f} MB)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
60
iqpilot/selfdrive/iqmodeld/tools/egpu_host_mock.py
Normal file
60
iqpilot/selfdrive/iqmodeld/tools/egpu_host_mock.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
DEFAULT_ARCH = "gfx1200"
|
||||
MOCK_DEV = "MOCKUSB+AMD:LLVM"
|
||||
|
||||
|
||||
def tinygrad_tree() -> str:
|
||||
override = os.environ.get("IQ_TINYGRAD_TREE")
|
||||
if override:
|
||||
return override
|
||||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
root = os.path.abspath(os.path.join(here, "..", "..", "..", ".."))
|
||||
return os.path.join(root, "components", "tinygrad")
|
||||
|
||||
|
||||
def activate(arch: str = DEFAULT_ARCH, execute: bool = False) -> None:
|
||||
assert "tinygrad" not in sys.modules, "egpu_host_mock.activate must run before tinygrad is imported"
|
||||
os.environ["DEV"] = f"{MOCK_DEV}:{arch}"
|
||||
tree = tinygrad_tree()
|
||||
if tree not in sys.path:
|
||||
sys.path.insert(0, tree)
|
||||
from tinygrad.runtime.autogen import libc
|
||||
if sys.platform == "darwin":
|
||||
# A Homebrew-LLVM gfx1200 kernel (no s_code_end padding) hung a real dock; ship only container-built artifacts.
|
||||
print("egpu_host_mock: native macOS LLVM output is for tests only; use scripts/iqpilot/host_egpu_compile_docker.sh for artifacts",
|
||||
file=sys.stderr)
|
||||
|
||||
def memfd_create(name, flags):
|
||||
fd, path = tempfile.mkstemp(prefix=b"iq_mock_" + bytes(name) + b"_")
|
||||
os.unlink(path)
|
||||
return fd
|
||||
libc.memfd_create = memfd_create
|
||||
if not hasattr(libc, "MFD_CLOEXEC"):
|
||||
libc.MFD_CLOEXEC = 1
|
||||
if not execute:
|
||||
import ctypes
|
||||
from test.mockgpu.amd import amdgpu
|
||||
amdgpu.remu.run_asm = lambda *args, **kwargs: 0
|
||||
pm4_wait = amdgpu.PM4Executor._exec_wait_reg_mem
|
||||
sdma_poll = amdgpu.SDMAExecutor._execute_poll_regmem
|
||||
|
||||
# Without kernel execution no memory wait carries information; a blocked wait would need a host write to re-poll it.
|
||||
def pm4_wait_passthrough(self, n):
|
||||
if not pm4_wait(self, n):
|
||||
self.rptr[0] += 7
|
||||
return True
|
||||
|
||||
def sdma_poll_passthrough(self):
|
||||
if not sdma_poll(self):
|
||||
self.rptr[0] += ctypes.sizeof(amdgpu.sdma_pkts.poll_regmem)
|
||||
return True
|
||||
amdgpu.PM4Executor._exec_wait_reg_mem = pm4_wait_passthrough
|
||||
amdgpu.SDMAExecutor._execute_poll_regmem = sdma_poll_passthrough
|
||||
64
iqpilot/selfdrive/iqmodeld/tools/oob_rewrite.py
Normal file
64
iqpilot/selfdrive/iqmodeld/tools/oob_rewrite.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import pickletools
|
||||
import struct
|
||||
|
||||
from iqpilot.selfdrive.iqmodeld.egpu_policy import OOB_MAGIC
|
||||
|
||||
MIN_OOB_BYTES = 1 << 16
|
||||
NEXT_BUFFER = b"\x97"
|
||||
READONLY_BUFFER = b"\x98"
|
||||
|
||||
|
||||
def rewrite_oob(src: str, dst: str, min_bytes: int = MIN_OOB_BYTES) -> tuple[int, int]:
|
||||
# tinygrad pickles device buffers as PickleBuffers, which land in-band as BYTEARRAY8/BINBYTES8
|
||||
# without a buffer_callback; moving those opcodes out-of-band is byte-for-byte what a protocol-5
|
||||
# dump with a buffer_callback produces, so nothing has to be unpickled (no dock needed).
|
||||
with open(src, "rb") as f:
|
||||
data = f.read()
|
||||
ops = list(pickletools.genops(data))
|
||||
proto = next((arg for op, arg, _ in ops if op.name == "PROTO"), 0)
|
||||
if proto < 5:
|
||||
raise ValueError(f"{src} is pickle protocol {proto}; out-of-band buffers need protocol 5")
|
||||
moved = 0
|
||||
tmp = dst + ".part"
|
||||
with open(tmp, "wb") as out, open(tmp + ".buf", "wb") as bufs:
|
||||
ops_stream = bytearray()
|
||||
for i, (op, arg, pos) in enumerate(ops):
|
||||
end = ops[i + 1][2] if i + 1 < len(ops) else len(data)
|
||||
if op.name in ("BYTEARRAY8", "BINBYTES8", "BINBYTES") and len(arg) >= min_bytes:
|
||||
ops_stream += NEXT_BUFFER
|
||||
if op.name != "BYTEARRAY8":
|
||||
ops_stream += READONLY_BUFFER
|
||||
bufs.write(struct.pack("<q", len(arg)))
|
||||
bufs.write(arg)
|
||||
moved += 1
|
||||
else:
|
||||
ops_stream += data[pos:end]
|
||||
out.write(OOB_MAGIC)
|
||||
out.write(struct.pack("<q", len(ops_stream)))
|
||||
out.write(ops_stream)
|
||||
with open(tmp, "ab") as out, open(tmp + ".buf", "rb") as bufs:
|
||||
while chunk := bufs.read(1 << 24):
|
||||
out.write(chunk)
|
||||
os.remove(tmp + ".buf")
|
||||
os.replace(tmp, dst)
|
||||
return moved, len(ops)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
p = argparse.ArgumentParser()
|
||||
p.add_argument("src")
|
||||
p.add_argument("dst")
|
||||
args = p.parse_args()
|
||||
moved, total = rewrite_oob(args.src, args.dst)
|
||||
print(f"{args.dst}: moved {moved} buffers out-of-band ({total} opcodes, {os.path.getsize(args.dst) / 1e6:.1f} MB)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user