a5823b4d
feat(m9): the control that makes leg 1 mean something
a73x 2026-08-08 17:15
Commit message
test/wan.sh
| Old | New | ||
|---|---|---|---|
| @@ -951,6 +951,29 @@ def cmd_predict(argv): | |||
| 951 | if c.wait_for(lambda b, w=run: w in b, timeout=20.0) is None: | 951 | if c.wait_for(lambda b, w=run: w in b, timeout=20.0) is None: |
| 952 | fail("predict: rep %d never echoed authoritatively" % i) | 952 | fail("predict: rep %d never echoed authoritatively" % i) |
| 953 | auth.append((now() - t0) * 1000.0) | 953 | auth.append((now() - t0) * 1000.0) |
| 954 | # The control the criterion asks for: input prediction does NOT | ||
| 955 | # apply to, timed on the same connection in the same run. | ||
| 956 | # | ||
| 957 | # "Echo-off context" is the plan's wording and it cannot be timed as | ||
| 958 | # written — with echo off nothing is painted at all, so there is no | ||
| 959 | # arrival to put a clock on. The measurable form of the same claim is | ||
| 960 | # input the overlay REFUSES: two characters in one write reach the | ||
| 961 | # client as one chunk, which is refused for being multi-byte, so the | ||
| 962 | # glyphs cannot appear until the daemon answers. If prediction were | ||
| 963 | # secretly doing the work in the numbers above, these would be fast | ||
| 964 | # too. They are not, and that is the point of measuring them. | ||
| 965 | off = [] | ||
| 966 | for i in range(min(reps, 10)): | ||
| 967 | pair = bytes("Q%d" % i, "ascii") | ||
| 968 | start = len(c.buf) | ||
| 969 | t0 = now() | ||
| 970 | c.send(pair) # ONE write, so ONE read at the far end | ||
| 971 | if c.wait_for(lambda b, w=pair, s=start: w in b[s:], | ||
| 972 | timeout=20.0) is None: | ||
| 973 | fail("predict: unpredicted control %d never painted" % i) | ||
| 974 | off.append((now() - t0) * 1000.0) | ||
| 975 | c.drain(0.1) | ||
| 976 | |||
| 954 | # Deliberately no EOF to end `cat`. Ending it means the next | 977 | # Deliberately no EOF to end `cat`. Ending it means the next |
| 955 | # keystroke lands at a bash prompt, which is a different termios and | 978 | # keystroke lands at a bash prompt, which is a different termios and |
| 956 | # therefore a different prediction tier — and over a 150ms path the | 979 | # therefore a different prediction tier — and over a 150ms path the |
| @@ -965,8 +988,10 @@ def cmd_predict(argv): | |||
| 965 | st = predict_stats(errlog) | 988 | st = predict_stats(errlog) |
| 966 | report("predict", pred, **{k: st.get(k, -1) for k in | 989 | report("predict", pred, **{k: st.get(k, -1) for k in |
| 967 | ("made", "displayed", "confirmed", | 990 | ("made", "displayed", "confirmed", |
| 968 | "contradicted", "expired", "abandoned")}) | 991 | "contradicted", "expired", "abandoned", |
| 992 | "suppressed")}) | ||
| 969 | report("predictauth", auth) | 993 | report("predictauth", auth) |
| 994 | report("predictoff", off) | ||
| 970 | 995 | ||
| 971 | 996 | ||
| 972 | def cmd_predictburst(argv): | 997 | def cmd_predictburst(argv): |
| @@ -1422,7 +1447,7 @@ if [ -n "$(val predict predict med)" ]; then | |||
| 1422 | echo | 1447 | echo |
| 1423 | echo "M9 prediction (netem 150ms egress):" | 1448 | echo "M9 prediction (netem 150ms egress):" |
| 1424 | printf ' %-34s %8s %8s %8s %5s\n' "" min med max n | 1449 | printf ' %-34s %8s %8s %8s %5s\n' "" min med max n |
| 1425 | for name in baseline predict predictauth predictburst; do | 1450 | for name in baseline predict predictoff predictauth predictburst; do |
| 1426 | [ -n "$(val predict "$name" med)" ] || continue | 1451 | [ -n "$(val predict "$name" med)" ] || continue |
| 1427 | printf ' %-34s %8s %8s %8s %5s\n' "$name" \ | 1452 | printf ' %-34s %8s %8s %8s %5s\n' "$name" \ |
| 1428 | "$(val predict "$name" min)" "$(val predict "$name" med)" \ | 1453 | "$(val predict "$name" min)" "$(val predict "$name" med)" \ |
| @@ -1436,10 +1461,20 @@ if [ -n "$(val predict predict med)" ]; then | |||
| 1436 | if awk -v r="$P_RTT" 'BEGIN{exit !(r < 50)}'; then | 1461 | if awk -v r="$P_RTT" 'BEGIN{exit !(r < 50)}'; then |
| 1437 | echo " leg 1: NOT EXERCISED — round trip $P_RTT ms is below the 50ms floor" | 1462 | echo " leg 1: NOT EXERCISED — round trip $P_RTT ms is below the 50ms floor" |
| 1438 | elif awk -v m="$P_MED" 'BEGIN{exit !(m <= 30)}'; then | 1463 | elif awk -v m="$P_MED" 'BEGIN{exit !(m <= 30)}'; then |
| 1464 | P_OFF="$(val predict predictoff med)" | ||
| 1439 | echo " leg 1: predicted paint med $P_MED <= 30ms -> PASS (round trip $P_RTT ms)" | 1465 | echo " leg 1: predicted paint med $P_MED <= 30ms -> PASS (round trip $P_RTT ms)" |
| 1440 | echo " control: the SAME keystroke's authoritative echo med $P_AUTH ms," | 1466 | echo " control, unpredicted input (refused as multi-byte): med $P_OFF ms" |
| 1441 | echo " which tracks the measured round trip — so this harness is timing" | 1467 | echo " control, same keystroke's authoritative echo: med $P_AUTH ms" |
| 1442 | echo " the path, and the predicted number is independent of it." | 1468 | echo " Both track the measured round trip while the predicted number does" |
| 1469 | echo " not, which is what rules out the harness having timed the hardware." | ||
| 1470 | # A control that came back fast would mean the "unpredicted" input was | ||
| 1471 | # being predicted after all, and leg 1's number would be meaningless. | ||
| 1472 | if [ -n "$P_OFF" ] && awk -v o="$P_OFF" -v r="$P_RTT" 'BEGIN{exit !(o < r/2)}'; then | ||
| 1473 | echo " leg 1 CONTROL FAILED: unpredicted input came back in $P_OFF ms, well" | ||
| 1474 | echo " under the $P_RTT ms path — it cannot have waited for the daemon," | ||
| 1475 | echo " so the comparison above proves nothing." | ||
| 1476 | FAILED=1 | ||
| 1477 | fi | ||
| 1443 | else | 1478 | else |
| 1444 | echo " leg 1: predicted paint med $P_MED > 30ms -> FAIL (round trip $P_RTT ms)" | 1479 | echo " leg 1: predicted paint med $P_MED > 30ms -> FAIL (round trip $P_RTT ms)" |
| 1445 | FAILED=1 | 1480 | FAILED=1 |
| @@ -1455,6 +1490,18 @@ if [ -n "$(val predict predict med)" ]; then | |||
| 1455 | if [ -n "$B_MADE" ]; then | 1490 | if [ -n "$B_MADE" ]; then |
| 1456 | echo " counters after $REPS_BURST bursts: made=$B_MADE confirmed=$B_CONF" \ | 1491 | echo " counters after $REPS_BURST bursts: made=$B_MADE confirmed=$B_CONF" \ |
| 1457 | "contradicted=$B_CONTRA expired=$B_EXP abandoned=$B_ABND pending=$B_PEND" | 1492 | "contradicted=$B_CONTRA expired=$B_EXP abandoned=$B_ABND pending=$B_PEND" |
| 1493 | # The ceiling these numbers live under, printed WITH them because it | ||
| 1494 | # is what decides whether they are reproducible on a slower path. A | ||
| 1495 | # prediction is retired unanswered after expire_after_ms; the wait it | ||
| 1496 | # actually has to survive is the round trip plus however long the | ||
| 1497 | # burst's later keystrokes queue behind the earlier ones. Measured | ||
| 1498 | # here as the burst's own convergence time. Cross this and the | ||
| 1499 | # predictions expire mid-burst and the counters collapse — which is | ||
| 1500 | # exactly what happened at 400ms each way in the e2e suite. | ||
| 1501 | B_CONVMED="$(val predict predictburst med)" | ||
| 1502 | echo " headroom: burst converged in ${B_CONVMED}ms (round trip $P_RTT ms +" | ||
| 1503 | echo " the burst's own typing span) against the overlay's 1000ms" | ||
| 1504 | echo " expiry bound — the margin a slower path spends first" | ||
| 1458 | # Leg 2, in this harness's substituted form. Comparing the client's | 1505 | # Leg 2, in this harness's substituted form. Comparing the client's |
| 1459 | # paint stream to `muxd dump` byte for byte needs a second terminal | 1506 | # paint stream to `muxd dump` byte for byte needs a second terminal |
| 1460 | # emulator in the harness — the same narrowing recorded for M7 at | 1507 | # emulator in the harness — the same narrowing recorded for M7 at |