Download scripts/run_chip8_lm.py from StarAtNyte1/beam-boot: direct link, hf CLI and curl.
- Browser
- Download file 6.1 kB
-
https://huggingface.co/StarAtNyte1/beam-boot/resolve/main/scripts/run_chip8_lm.py
- Command line
-
hf download hf://StarAtNyte1/beam-boot/scripts/run_chip8_lm.py
-
curl -L -o run_chip8_lm.py https://huggingface.co/StarAtNyte1/beam-boot/resolve/main/scripts/run_chip8_lm.py
6.1 kB
| """Run SUPERBEAM inside the constructed BeamForCausalLM (beamboot/chip8_lm.py) on the GPU. | |
| .venv-gpu/bin/python scripts/run_chip8_lm.py # first 5 frames, verified | |
| .venv-gpu/bin/python scripts/run_chip8_lm.py --frames 171 | |
| Keys come from roms/superbeam_inputs.json (the keyboard device injects them as KEY tokens at | |
| frame boundaries). Every token, every instruction's state (registers, stack, memory, display) and | |
| every frame is checked against beamboot/chip8.py, and the frames also against | |
| demo/superbeam_frames.json. Writes demo/superbeam_model_frames.json: | |
| {rom, ipf, frames_run, inputs, verified, mismatch, matches_superbeam_frames_json, n_tokens, | |
| prompt_tokens, decode_seconds, tok_per_s, device, model: {...}, frames: [[32 hex rows], ...]} | |
| Prints tok/s and an ETA for all 171 frames (step time measured at longer cache lengths). | |
| """ | |
| import argparse | |
| import json | |
| import os | |
| import sys | |
| import time | |
| ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| sys.path.insert(0, ROOT) | |
| import torch # noqa: E402 | |
| from beamboot import chip8_lm as C # noqa: E402 | |
| ROM = os.path.join(ROOT, 'roms', 'superbeam.ch8') | |
| INPUTS = os.path.join(ROOT, 'roms', 'superbeam_inputs.json') | |
| REF_FRAMES = os.path.join(ROOT, 'demo', 'superbeam_frames.json') | |
| OUT = os.path.join(ROOT, 'demo', 'superbeam_model_frames.json') | |
| FULL = 171 | |
| def total_tokens(rom, events, frames, ipf): | |
| """Stream length (prompt excluded) for `frames` frames, from the reference.""" | |
| keys = C.key_tokens(events) | |
| n, nt = 0, 0 | |
| for kind, ts, _ in C._ref_records(rom, ipf, 0, lambda fr: keys.get(fr, ())): | |
| n += len(ts) | |
| nt += kind == 'tick' | |
| if nt == frames: | |
| return n | |
| def save(s, model, a, events, ref_frames): | |
| res = s.result(tokens=False) | |
| match = (res['frame_rows'] == ref_frames[:res['frames']]) if ref_frames else None | |
| out = dict(rom='roms/superbeam.ch8', inputs_file=os.path.relpath(a.inputs, ROOT), ipf=a.ipf, frames_run=res['frames'], | |
| inputs=[e for e in events if e[0] < res['frames']], verified=res['verified'], | |
| mismatch=res['mismatch'], matches_superbeam_frames_json=match, n_tokens=res['n_tokens'], | |
| prompt_tokens=res['prompt_tokens'], decode_seconds=round(res['decode_seconds'], 2), | |
| tok_per_s=round(res['tok_per_s'], 1), device=torch.cuda.get_device_name(), | |
| model=dict(cls=type(model).__name__, layers=model.config.num_hidden_layers, | |
| hidden=model.config.hidden_size, vocab=model.config.vocab_size, dtype='float32', | |
| attn=model.config._attn_implementation, decoder='GraphDecoder (CUDA graphs)'), | |
| frames=res['frame_rows']) | |
| with open(a.out + '.tmp', 'w') as f: | |
| json.dump(out, f) | |
| os.replace(a.out + '.tmp', a.out) | |
| print('wrote %s (%d frames)' % (os.path.relpath(a.out, ROOT), res['frames']), flush=True) | |
| res['matches'] = match | |
| return res | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument('--frames', type=int, default=5) | |
| ap.add_argument('--ipf', type=int, default=300) | |
| ap.add_argument('--no-eta', action='store_true') | |
| ap.add_argument('--inputs', default=INPUTS, help='key events list, or a schedule file with an "events" key') | |
| ap.add_argument('--out', default=OUT) | |
| ap.add_argument('--save-every', type=int, default=5, help='rewrite --out every N frames') | |
| a = ap.parse_args() | |
| rom = open(ROM, 'rb').read() | |
| events = json.load(open(a.inputs)) | |
| if isinstance(events, dict): | |
| events = events['events'] | |
| scripted = os.path.abspath(a.inputs) == os.path.abspath(INPUTS) | |
| ref_frames = json.load(open(REF_FRAMES))['frames'] if scripted else None | |
| t0 = time.perf_counter() | |
| model = C.get_model(a.ipf, 'cuda') | |
| print('model built in %.1fs: %s, %d layers, hidden %d, vocab %d, %.1fM params, %s' | |
| % (time.perf_counter() - t0, type(model).__name__, model.config.num_hidden_layers, | |
| model.config.hidden_size, model.config.vocab_size, | |
| sum(p.numel() for p in model.parameters()) / 1e6, torch.cuda.get_device_name())) | |
| s = C.Session(rom, 'cuda', a.ipf, verify=True, model=model) | |
| print('prompt %d tokens, prefill %.2fs' % (s.prompt_tokens, s.prefill_seconds)) | |
| by = {} | |
| for g, k, act in events: | |
| by.setdefault(g, []).append((k, act == 'down')) | |
| for g in range(a.frames): | |
| n0, t1 = len(s.tokens), time.perf_counter() | |
| s.advance(by.get(g, ())) | |
| dt = time.perf_counter() - t1 | |
| ok = 'ok' if s.mismatch is None else 'MISMATCH %s' % s.mismatch | |
| same = (s.result(tokens=False)['frame_rows'][-1] == ref_frames[g]) if ref_frames else 'n/a' | |
| print('frame %3d: %5d tokens %6.1fs %6.1f tok/s verify %s frame==superbeam_frames.json %s' | |
| % (g, len(s.tokens) - n0, dt, (len(s.tokens) - n0) / dt, ok, same)) | |
| if s.mismatch is not None: | |
| break | |
| if a.save_every and (g + 1) % a.save_every == 0: | |
| save(s, model, a, events, ref_frames) | |
| res = save(s, model, a, events, ref_frames) | |
| match = res['matches'] | |
| print('frames %d, tokens %d, decode %.1fs, %.1f tok/s, verified %s, frames match json %s' | |
| % (res['frames'], res['n_tokens'], res['decode_seconds'], res['tok_per_s'], res['verified'], match)) | |
| if a.no_eta: | |
| return | |
| total = total_tokens(rom, events, FULL, a.ipf) + s.prompt_tokens | |
| done = s.prompt_tokens + len(s.tokens) | |
| pts = [done + 64] + [x for x in (16384, 32768, 65536, 131072) if done + 64 < x < total] + [total] | |
| times = [(L, s.dec.time_at(L)) for L in pts] | |
| print('step time vs cache length: ' + ', '.join('%d: %.1f ms (%.0f tok/s)' % (L, 1e3 * t, 1 / t) | |
| for L, t in times)) | |
| eta = 0.0 | |
| for (l0, t0_), (l1, t1_) in zip(times, times[1:]): | |
| eta += (l1 - l0) * (t0_ + t1_) / 2 | |
| print('171 frames = %d tokens (incl. prompt); remaining ETA %.1f min, whole run ~%.1f min' | |
| % (total, eta / 60, (eta + res['decode_seconds']) / 60)) | |
| if __name__ == '__main__': | |
| main() | |