Skip to content

hyperloom custom workload: every measurement writes the same log filenames into RESULT_DIR, so a later arm silently destroys an earlier arm's raw evidence #892

Closed

Description

@bong-water-water-bong

tools/hyperloom/custom/benchmark.py writes its three raw logs to fixed names inside RESULT_DIR, independent of the
measurement's own RESULT_FILENAME:

  • serve.log — serve_measure() ((out_dir / "serve.log").open("w"), and placement_summary() reads it back)
  • llama-bench.log — run(...) ((out_dir / f"{name}.log").write_text(...), name is the leg, not the measurement)
  • kld.log / kld-record.log — kld() via run(...)

So two measurements that share one RESULT_DIR — which is the normal shape for a run, and is exactly what a
HL_PAIR A/B pair does — collide on all three. The result JSONs are distinct (<RESULT_FILENAME>.json), so the
numbers survive while the logs that produced them do not, and the loss is silent: nothing errors, the file simply
holds the later arm's text.

Reproducer (no GPU, no model, no server — just the writer):

import pathlib, sys, tempfile
sys.path.insert(0, "tools/hyperloom/custom")
import benchmark as B
with tempfile.TemporaryDirectory() as td:
    root = pathlib.Path(td)
    B.run(["sh", "-c", "echo FIRST-MEASUREMENT"],  "llama-bench", 10, root)
    B.run(["sh", "-c", "echo SECOND-MEASUREMENT"], "llama-bench", 10, root)
    print(sorted(p.name for p in root.iterdir()))          # ['llama-bench.log']
    print((root / "llama-bench.log").read_text().strip())   # SECOND-MEASUREMENT
files in RESULT_DIR: ['llama-bench.log']
llama-bench.log now holds: SECOND-MEASUREMENT

Measured cost, on strixhalo with this workload: the 2026-11-02 19:01 arm overwrote the 18:45 baseline's serve.log,
llama-bench.log and kld.log in ~/hyperloom-ws/window-20261102/. The JSONs and every number quoted in
~/briefs/THYPERLOOM-STATE.md are intact (they were read before the overwrite), but the raw leg output behind them is
gone. The same collision would have hit both halves of a pair, and a driver killed mid-arm (e.g. a reboot) leaves a
partial serve.log that the next arm then overwrites, which is how a partial leg gets mistaken for a complete one.

Workaround in use outside the repo: the harness moves the three files into logs-<result>-<hhmmss>/ after each arm.
That is a workaround for a producer-side defect, and it cannot help a run driven straight through hyperloom optimize.

Activity

  1. bong-water-water-bong commented on Nov 3, 2026

    @bong-water-water-bong
    CollaboratorAuthor

    Triaged: reproduced at 7690a8c3, and the fix (#893) is already up

    Checked at origin/main = 7690a8c3, not against the report: the three writers are exactly as filed, and they are the
    only paths that bypass the per-measurement name —

    writer file:line at 7690a8c3 name it uses
    run() tools/hyperloom/custom/benchmark.py:246 out_dir / f"{name}.log" — name is the leg (llama-bench, kld, kld-record), never the measurement
    serve_measure() :328 (writer), :361 (read-back) out_dir / "serve.log"
    kld() :390 → run() kld / kld-record

    while everything that is per-measurement is keyed on RESULT_FILENAME (:405-406 RESULT_DIR, :454 f"{name}.json").
    That asymmetry is the whole bug: the numbers are named, the evidence behind them is not.

    Reproduced, host-only, no model, no GPU — the issue's own scenario against origin/main's benchmark.py, two
    run(…, "llama-bench", …) calls in one RESULT_DIR:

    files: ['llama-bench.log']
    llama-bench.log holds: SECOND-MEASUREMENT
    

    One thing the issue does not say, and it is the reason #893's diff has to touch two lines, not one

    serve_measure() reads back the same fixed path it wrote: :328 opens out_dir/"serve.log" for writing and :361
    returns placement_summary(out_dir / "serve.log"). So the coupling is not only archive-deep. Sequential arms lose
    evidence (as filed); two arms sharing one RESULT_DIR concurrently would have one arm's placement_summary() parse the
    other arm's log, i.e. the wrongness reaches the numbers. I have not seen that happen in THYPERLOOM-STATE.md and am not
    claiming it did — the point is that any fix which redirects the writer and leaves the reader alone keeps this second failure
    mode while looking fixed.

    PR #893 does redirect both (placement_summary(log_dir / "serve.log")), moves the directory creation into one helper
    log_dir_for(out_dir, name) → RESULT_DIR/logs-<RESULT_FILENAME>/, and asserts the pair case in
    tests/hyperloom_benchmark_test.py. I ran its test on this box (host-only):

    python3 tests/hyperloom_benchmark_test.py   ->  hyperloom_benchmark_test: OK   (rc 0)
    [1bit-hl] llama-bench: rc=0 … -> <tmp>/logs-arm-A/llama-bench.log
    [1bit-hl] llama-bench: rc=0 … -> <tmp>/logs-arm-B/llama-bench.log
    

    Two residuals, both small and neither blocking

    1. The out-of-repo workaround now writes a second naming scheme. The harness on this box moves the three files to
      logs-<result>-<hhmmss>/ after each arm; the producer will write logs-<RESULT_FILENAME>/. Two schemes for one
      directory it does not control — the mover should be retired when hyperloom custom: keep each measurement's raw logs (they overwrote each other) #893 lands, or the next reader has to know both.
    2. A killed arm still leaves a log that looks complete. hyperloom custom: keep each measurement's raw logs (they overwrote each other) #893 fixes the collision, not the "partial leg mistaken for a
      complete one" case the report names as the driver-kill scenario: nothing marks an arm as finished. A .rc/sentinel
      written at arm end (one line, in the same helper) would make that distinguishable; not requested here, and not needed
      for this issue to close.

    What happens next: #893 is the fix and it reads as complete from my side (closes #892 in its body, so it closes itself on
    merge). The ~/hyperloom-ws/window-20261102 logs lost at 19:01 are not recoverable — the JSONs quoted in
    THYPERLOOM-STATE.md are, and that is the trade the fix makes permanent. Labelled bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions