diff options
| author | eunhhu <[email protected]> | 2026-09-24 17:14:54 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-24 17:14:54 +0100 |
| commit | b1ce1927022d374198441afafeb27199fc8a052a (patch) | |
| tree | 6021f175f4c67ed03b44240eaa2f90d846f12f6f /research/experiments/006-native-save-latency/measure.py | |
| parent | d34f9426d5317a1948f12f229439946728bc342a (diff) | |
| download | pure-b1ce1927022d374198441afafeb27199fc8a052a.tar.gz pure-b1ce1927022d374198441afafeb27199fc8a052a.zip | |
Establish native micro runtime checkpoint
Diffstat (limited to 'research/experiments/006-native-save-latency/measure.py')
| -rw-r--r-- | research/experiments/006-native-save-latency/measure.py | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/research/experiments/006-native-save-latency/measure.py b/research/experiments/006-native-save-latency/measure.py new file mode 100644 index 0000000..f1e2a00 --- /dev/null +++ b/research/experiments/006-native-save-latency/measure.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +"""Time durable one-byte saves in the actual native terminal document app.""" +import argparse +import fcntl +import hashlib +import json +import os +from pathlib import Path +import platform +import pty +import select +import statistics +import struct +import subprocess +import tempfile +import termios +import time + + +def run(*args): + return subprocess.run([str(arg) for arg in args], check=True, + capture_output=True, timeout=30) + + +def frame(master, process): + output = bytearray() + deadline = time.monotonic() + 15 + while time.monotonic() < deadline: + if select.select([master], [], [], 0.5)[0]: + output.extend(os.read(master, 65536)) + if b'\x1b[?25h' in output: + return bytes(output) + if process.poll() is not None: + break + raise AssertionError((process.poll(), output[-1000:])) + + +def one_session(cli, tui, root, size, iterations): + root.mkdir(parents=True) + store = root / 'store' + body = b'A' * size + insert = root / 'initial.bin' + insert.write_bytes(body) + assert json.loads(run(cli, '--store', store, 'edit', 'document', + 0, 0, 0, insert).stdout)['revision'] == '1' + master, slave = pty.openpty() + fcntl.ioctl(slave, termios.TIOCSWINSZ, struct.pack('HHHH', 24, 80, 0, 0)) + original = termios.tcgetattr(slave) + process = subprocess.Popen([str(tui), '--store', str(store), 'document'], + stdin=slave, stdout=slave, stderr=slave, + env={**os.environ, 'LANG': 'C.UTF-8', 'LC_ALL': 'C.UTF-8'}) + samples = [] + try: + assert b'Pure | document' in frame(master, process) + for index in range(iterations): + replacement = b'B' if index % 2 == 0 else b'A' + os.write(master, b'\x7f') + frame(master, process) + os.write(master, replacement) + frame(master, process) + start = time.perf_counter_ns() + os.write(master, b'\x13') + shown = frame(master, process) + elapsed = time.perf_counter_ns() - start + expected = f'Saved 1-byte edit at byte {size - 1}'.encode() + revision = f'revision {index + 2}'.encode() + assert expected in shown and revision in shown, shown[-1500:] + samples.append(elapsed) + os.write(master, b'\x11') + process.wait(timeout=8) + assert process.returncode == 0 + assert termios.tcgetattr(slave) == original + finally: + if process.poll() is None: + process.kill() + process.wait(timeout=5) + os.close(master) + os.close(slave) + final = run(cli, '--store', store, 'show', 'document') + expected_body = body[:-1] + (b'B' if iterations % 2 else b'A') + assert final.stdout == expected_body + assert json.loads(final.stderr)['revision'] == str(iterations + 1) + return {'samples_ns': samples, 'median_ns': int(statistics.median(samples)), + 'final_revision': iterations + 1, + 'body_sha256': hashlib.sha256(final.stdout).hexdigest()} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--flat-cli', type=Path, required=True) + parser.add_argument('--flat-tui', type=Path, required=True) + parser.add_argument('--paged-cli', type=Path, required=True) + parser.add_argument('--paged-tui', type=Path, required=True) + parser.add_argument('--scratch-parent', type=Path, required=True) + parser.add_argument('--rounds', type=int, default=3) + parser.add_argument('--edits', type=int, default=8) + parser.add_argument('--output', type=Path) + args = parser.parse_args() + if args.rounds < 1 or args.edits < 1 or (args.output and args.output.exists()): + parser.error('rounds and edits must be positive; output must not exist') + binaries = {name: (cli.resolve(), tui.resolve()) for name, cli, tui in + (('flat', args.flat_cli, args.flat_tui), + ('paged', args.paged_cli, args.paged_tui))} + parent = args.scratch_parent.resolve() + parent.mkdir(parents=True, exist_ok=True) + filesystem = run('findmnt', '-T', parent, '-no', 'SOURCE,FSTYPE').stdout.decode().strip() + raw = {name: {'256': [], '1048576': []} for name in binaries} + with tempfile.TemporaryDirectory(prefix='pure-native-save-', dir=parent) as directory: + root = Path(directory) + for size in (256, 1_048_576): + for round_number in range(args.rounds): + order = ('flat', 'paged') if round_number % 2 == 0 else ('paged', 'flat') + for name in order: + cli, tui = binaries[name] + raw[name][str(size)].append(one_session( + cli, tui, root / f'{size}-{round_number}-{name}', size, args.edits)) + result = {'platform': platform.platform(), 'filesystem': filesystem, + 'workload': 'one-byte final-character replacement, Ctrl+S to complete visible frame; ' + 'includes full draft comparison, runtime, durable store and redraw; ' + 'initial creation and final independent CLI read excluded', + 'rounds': args.rounds, 'edits_per_session': args.edits, + 'binaries': {name: {'cli_sha256': hashlib.sha256(cli.read_bytes()).hexdigest(), + 'tui_sha256': hashlib.sha256(tui.read_bytes()).hexdigest()} + for name, (cli, tui) in binaries.items()}, + 'runs': raw, + 'median_ns': {name: {size: int(statistics.median( + sample for session in sessions for sample in session['samples_ns'])) + for size, sessions in sizes.items()} for name, sizes in raw.items()}} + encoded = json.dumps(result, indent=2) + '\n' + if args.output: + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(encoded) + print(json.dumps({'platform': result['platform'], 'filesystem': filesystem, + 'median_ns': result['median_ns'], 'rounds': args.rounds, + 'edits_per_session': args.edits})) + + +if __name__ == '__main__': + main() |
