#!/usr/bin/env python3 """DOSBox vs dosemu on the real workload: GSPA assembling TMS34010 source. Workload A: gspa t1 t1.obj t1.lst -l (4 KB region, listing) Workload B: gspa -q rt16 rt16.obj (16 KB region, no listing) Both headless: DOSBox with SDL dummy drivers and core=dynamic/cycles=max; dosemu in -t (terminal/S-Lang) mode. Three measured runs each, plus one warm-up. Raw COFF md5 differs only in the 4-byte time stamp, so a timestamp-normalised md5 is also reported. """ import hashlib import os import statistics import subprocess import time from pathlib import Path ROOT = Path(__file__).resolve().parent BENCH = ROOT / "bench" DHOME = ROOT / "dosemu-home" DBENCH = DHOME / ".dosemu" / "drive_c" / "bench" RUNS = 3 def norm_md5(p): if not p.exists(): return "-" b = bytearray(p.read_bytes()) if len(b) >= 8: b[4:8] = b"\0\0\0\0" return hashlib.md5(bytes(b)).hexdigest() def raw_md5(p): return hashlib.md5(p.read_bytes()).hexdigest() if p.exists() else "-" def clean(d, base): for name in (base + ".OBJ", base + ".LST", "MACLIB.$$$", base.lower() + ".obj", base.lower() + ".lst", "maclib.$$$"): f = d / name if f.exists(): f.unlink() def run_dosbox(base, args): clean(BENCH, base) cmds = [] for _ in range(1): cmds += ["-c", "gspa " + args] env = dict(os.environ, SDL_VIDEODRIVER="dummy", SDL_AUDIODRIVER="dummy") t0 = time.perf_counter() rc = subprocess.run( ["timeout", "120", "dosbox", "-conf", str(ROOT / "dosbox-bench.conf"), "-noprimaryconf", "-nolocalconf", "-c", "mount c " + str(BENCH), "-c", "c:"] + cmds + ["-c", "exit", "-exit"], env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode dt = time.perf_counter() - t0 # DOSBox uppercases 8.3 names obj = BENCH / (base.upper() + ".OBJ") return dt, rc, raw_md5(obj), norm_md5(obj), obj.stat().st_size if obj.exists() else 0 def run_dosemu(base, args, bat): clean(DBENCH, base) script = "@echo off\r\nc:\r\ncd \\bench\r\ngspa %s\r\nexitemu\r\n" % args (DBENCH / bat).write_bytes(script.encode()) env = dict(os.environ, HOME=str(DHOME)) t0 = time.perf_counter() rc = subprocess.run( ["timeout", "120", "dosemu", "-t", "-quiet", "-E", r"c:\bench" + "\\" + bat], env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode dt = time.perf_counter() - t0 # dosemu/lredir keeps the lowercase names written by FreeCom obj = DBENCH / (base.lower() + ".obj") if not obj.exists(): obj = DBENCH / (base.upper() + ".OBJ") return dt, rc, raw_md5(obj), norm_md5(obj), obj.stat().st_size if obj.exists() else 0 def bench(name, base, args, bat): print("\n== %s == gspa %s" % (name, args)) run_dosbox(base, args) # warm-up run_dosemu(base, args, bat) results = {} for label, fn in (("dosbox", lambda: run_dosbox(base, args)), ("dosemu", lambda: run_dosemu(base, args, bat))): rows = [fn() for _ in range(RUNS)] results[label] = rows for dt, rc, raw, norm, size in rows: print(" %-7s %6.2fs rc=%d size=%d raw=%s norm=%s" % (label, dt, rc, size, raw[:8], norm[:8])) print(" %-7s median %.2fs" % (label, statistics.median(r[0] for r in rows))) n0 = {r[3] for r in results["dosbox"]} n1 = {r[3] for r in results["dosemu"]} print(" normalised object identical: %s (%s)" % (n0 == n1 and len(n0) == 1, n0)) if __name__ == "__main__": bench("A: 4 KB with listing", "t1", "t1 t1.obj t1.lst -l", "bench.bat") bench("B: 16 KB, no listing", "rt16", "-q rt16 rt16.obj", "bench16.bat")