# /// script
# requires-python = ">=3.10"
# ///
# Copyright (c) 2026 Aayush Sharma · SPDX-License-Identifier: MIT
# https://thedataengineering.com/lab-code/LICENSE.txt
"""Wall time minus CPU time.

10,000 small writes with an fsync after each, timed by the wall clock and by
CPU time; then the same writes with one fsync every 100 (a simple group
commit). On macOS, --full-fsync uses F_FULLFSYNC, which is what actually
reaches the device there.
"""
import argparse, fcntl, json, os, tempfile, time

p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
p.add_argument("--writes", type=int, default=10_000, help="Records to write.")
p.add_argument("--size", type=int, default=100, help="Bytes per record.")
p.add_argument("--group", type=int, default=100, help="Writes per fsync in the group-commit run.")
p.add_argument("--full-fsync", action="store_true", help="macOS: flush with F_FULLFSYNC instead of fsync.")
p.add_argument("--json", help="Also write the results to this JSON file.")
args = p.parse_args()

def sync(fd):
    if args.full_fsync: fcntl.fcntl(fd, fcntl.F_FULLFSYNC)
    else: os.fsync(fd)                                             # force the write to the disk

def loop(every):
    path = os.path.join(tempfile.mkdtemp(), "lab5.dat")
    fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
    w0, c0 = time.perf_counter(), time.process_time()
    for i in range(args.writes):
        os.write(fd, b"x" * args.size)
        if (i + 1) % every == 0: sync(fd)
    w1, c1 = time.perf_counter(), time.process_time()
    os.close(fd); os.remove(path)
    wall, cpu = w1 - w0, c1 - c0
    return {"run": f"fsync every {every} write{'s' if every > 1 else ''}", "wall_s": wall, "cpu_s": cpu, "waiting_pct": (1 - cpu / wall) * 100}

rows = [loop(1), loop(args.group)]
summary = {"speedup": rows[0]["wall_s"] / rows[1]["wall_s"], "cpu_ratio": rows[0]["cpu_s"] / rows[1]["cpu_s"]}
for r in rows: print(f"{r['run']:<22} wall={r['wall_s']:.2f}s  cpu={r['cpu_s']:.2f}s  waiting={r['waiting_pct']:.0f}%")
print(f"group commit: wall time {summary['speedup']:.0f}x lower, CPU time {summary['cpu_ratio']:.1f}x lower")

if args.json:
    with open(args.json, "w") as f:
        json.dump({"summary": summary, "tables": {"runs": rows}}, f)
