Skip to content

GH-224: parent waits for the solver child in poll() (removes the 10 ms timing floor) - #226

Merged
guluchen merged 3 commits into
mainfrom
fix/gh-224-wait-floor
Nov 4, 2026
Merged

guluchen merged 3 commits into
mainfrom
fix/gh-224-wait-floor

Conversation

@guluchen

@guluchen guluchen commented Nov 4, 2026

Copy link
Copy Markdown
Owner

Closes #224 (hub #15; related #217 / #220).

Change

solve_in_child_fork in src/core/distqldpc.cc used to poll the solver child with usleep(10000). The parent saw the child's exit only at the next 10 ms tick, so the wall time of fast runs was quantized into 10 ms buckets.

The parent now waits in poll() on two file descriptors: the bounds pipe and a SIGCHLD self-pipe.

  • The handler is installed only in the parent, after fork(), so the solver child is untouched.
  • A child that exits before the handler is installed is caught by the waitpid(WNOHANG) that precedes every poll().
  • The timeout is the remaining -cpu-lim time, capped at 100 ms as a safety net, or 1 ms if the self-pipe cannot be created.
  • After the bounds pipe reaches EOF, the parent stops polling it, so it does not spin while the child finishes exiting.

What stays the same:

  • the deadline -> SIGKILL -> TIMEOUT path;
  • the memory cap (prlimit applies to the process tree);
  • the protocol and all output lines.

The previous SIGCHLD disposition is restored afterwards. MODIFICATIONS.md is updated.

Tier 0

  • make test-cc: PASS on yfclab2 and on the Mac. The output equals main's except one timing-dependent counter, partE "levels abandoned": 37 on main vs 38 here. That test does not call the changed function.
  • make test-cc-flags: 566/566 on both hosts.
  • The smoke test and the partition test pass.
  • Behaviour, old vs new, with identical output lines:
    • -cpu-lim=3 TIMEOUT at 3.03 s with the same bounds (MaxSAT and CC);
    • a tighter prlimit --as gives the same UNKNOWN;
    • a child killed from outside gives UNKNOWN 6-8 ms later;
    • a child under SIGSTOP/SIGCONT finishes normally.

Tier 0 + Tier 1

Setup (yfclab2):

  • Main's current layout: 26 tier0 + 9 tier1 codes.
  • 5 seeds x {MaxSAT default, MaxSAT -no-card, CC default}.
  • Old binary (main fecee8c) vs new (2587b9d), interleaved on one pinned core per (code, mode, seed), 9 repetitions with rotating order.
  • Cores 72-101, each with an idle SMT sibling.
  • Ratio = 5-seed median of the repetition medians, new / old.
Tier Mode cells geomean worst cell cells > 1.25 verdict
0 CC 26 0.666 0.964 (AMC_330) 0 PASS
0 MaxSAT 26 0.849 1.032 (LP_238) 0 PASS
0 MaxSAT -no-card 26 0.853 1.086 (BB_30) 0 PASS
1 CC 9 0.879 0.995 (BB_288) 0 PASS
1 MaxSAT 9 0.978 0.987 (HGP_610) 0 PASS
1 MaxSAT -no-card 9 0.981 0.993 (LP_442) 0 PASS
  • Answers and logs. 9450 runs: all rc 0, and every answer equals its stem. stdout is byte-identical in every run of both arms for 522/525 units. The other 3 (GB_144_12_12 CC) differ only in where a c d_ub line falls among the level lines; main's own repetitions vary the same way.
  • Floor gone. Wait slack (wall - user - sys) of runs under 0.2 s has median / p90 11.9 / 16.2 ms on main and 4.8 / 7.8 ms here.
    • LP_442 CC wall times on main fall into two groups (27-31 ms and 34-44 ms); here they form one contiguous group, 25-33 ms.
    • BB_186 CC: 28-60 ms on main, 21-30 ms here.
  • MaxSAT CPU time. The 1-10 s MaxSAT cells also use about 2 % less CPU, because the parent no longer wakes 100 times a second on the solver's core.

Tiers 2-5: spot check only

By construction, Tiers 2-5 are unaffected: the solver child and the deadline path are unchanged, and the parent wakes less often than before. I did not run full Tiers 2-4 or the 6 h Tier 5. I spot-checked instead, interleaved with 3 seeds x 3 repetitions:

  • Tier 2: LP_544, TN_108 and BB_144_14_14 in all three modes. Geomean 0.952, worst cell 1.001.
  • Tier 3: TN_250 (50.6 s), LP_1020 and HGP_481, CC. Geomean 0.959, worst cell 1.002.
  • All answers are correct.

Evidence: optimization/experiments/GH-224/ (analysis.md, runner/, raw/).

🤖 Generated with Claude Code

yfc and others added 3 commits November 5, 2026 02:05
…stead of usleep(10000)

The parent noticed the child's exit only at the next 10 ms tick, which quantized
the wall time of fast runs into 10 ms buckets (LP_442 -engine=cc at ~23 or ~33 ms).
The parent now sleeps in poll() on the bounds pipe and a SIGCHLD self-pipe, with
the remaining -cpu-lim time (capped at 100 ms) as timeout. The deadline/SIGKILL
path, the child code and all output are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…heck, behaviour checks)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parent wait loop: remove the 10 ms usleep polling floor on fast runs

1 participant