aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHECKPOINT.md2
-rw-r--r--docs/design/025-batch-programs.md3
-rw-r--r--docs/research/134-batch-plans-stage-one.md72
-rw-r--r--research/experiments/041-batches/2026-10-08-batch-costs.jsonl6
-rw-r--r--research/experiments/041-batches/2026-10-08-counts.jsonl10
-rw-r--r--research/experiments/041-batches/2026-10-08-pairs.jsonl120
-rwxr-xr-xresearch/experiments/041-batches/runs.sh17
7 files changed, 227 insertions, 3 deletions
diff --git a/CHECKPOINT.md b/CHECKPOINT.md
index 9db7e80..654ce16 100644
--- a/CHECKPOINT.md
+++ b/CHECKPOINT.md
@@ -13,7 +13,7 @@
| 영역 | 확인한 것 | 근거 |
|---|---|---|
| 앱 표현 | 상태(수, `bool`, 정확한 64비트 정수 `i64`, text와 커서·마크, 레코드의 리스트와 키 있는 리스트, 인덱스), 입력(키, text, 필드 있는 메시지, 타이머), 원자적 이벤트 핸들러, 순수 보기(조건식, 표와 걸러진 표, count·sum·max·min·first), `fn`·`use` 라이브러리, text 비교, 시간(`now`, `every`, `floor`, `%`). 로드할 때 타입·스택·합류·커서와 인덱스의 소속을 모두 검증하고 이벤트의 실행량에 상한이 있다 | 008, [016](docs/design/016-bool-and-i64.md), aup `docs/design/002-dsl-direction.md`, aup `make check`(파이썬 참조 모델 대조, 진단 223건) |
-| 수명과 계산 | 값의 수명은 선언이 말한다: `state`(저장), `transient state`(앱이 도는 동안: 저장소에 쓰지 않고 다시 시작하면 초기값, SD 카드에서 쓰기 17배), `let`(이벤트 동안). 핸들러와 보기가 계산한다: 이벤트의 리스트(`let seen: list of Seen by node`), 세는 `for i in a .. b`, `break`, 8겹까지의 중첩, 그리고 `view 이름(인자) { … return … }`(스냅숏에서 돌아 소유자를 막지 않는다). 모든 반복은 시작할 때 횟수의 상한을 갖고, 모든 이벤트와 보기는 실행량 예산(정하지 않으면 2^28 명령)과 메모리 몫(2^28 바이트) 안에서 끝난다. 노드 10만·간선 100만의 BFS 한 이벤트가 HTTP로 90 ms였고(처음 397, 020 뒤 191; CPython 452, C 9.1, 123), 022 뒤 코어는 7.4 ms다(같은 프로세스의 C 9.9): 많이 찾는 저장된 리스트를 배열과 칸의 색인으로 펴고, 열을 값만큼 좁히고, 루프 묶음을 레지스터의 기계어로 돈다(리눅스 AArch64의 JIT; 다른 곳은 실행기, 답은 같다). 다만 C를 넘은 것은 미리 읽기 덕이고, 끄면 15.7 ms로 C의 1.57배다. 일곱 가지 일(실수 루프, 배낭, 정렬, 조인, 텍스트, 행렬, BFS)로 C와 견주면 124에서는 3–300배 느렸고, 023의 계층과 표현(HASHED: 차례를 어긴 키는 해시 표의 칸에, 위치가 필요할 때 정렬)으로 지금은 1.19–5.58배(기하 평균 1.92)다(125). Pure의 표현과 뜻대로 손으로 쓴 바닥은 C의 0.90배이고 뜻의 검사는 끌어내면 0–3%라, 막는 것은 실행 계층이다(124). 모든 블록을 SSA로 올리는 최적화 계층(023)의 첫 걸음들이 들어갔고, 텍스트(실행기로 돈다)와 BFS(계층이 덧붙이기를 못 한다)가 남았다. 기계어를 만들지 않는 길로, 루프를 회차 256개의 묶음으로 돌고 연산마다 빌드 때 만든 C 루프를 쓰는 묶음 실행기(024)를 시제품으로 재니 연산을 따로 해석하면 roots 0.99배, 배낭 0.71배(4바이트 레인), BFS 1.03배, join 1.77배, 행렬 곱 2.50배이고, 행렬 곱은 곱해 더하기를 합치면 0.94배였다(계층 1.20–2.46배, 기하 평균 1.92 대 1.58, 합친 꼴로 1.24; 041, 손으로 짠 천장). 1단계(세는 루프의 레인 꼴 몸체, 회차 1024개씩)가 들어가 knapsack이 실행기에서 1,041 ms에서 13.6 ms가 됐다(계층 7.5, 안쪽 루프는 1.17배). 묶음 루프가 없는 모듈의 실행기는 명령과 사이클이 그대로다(126). 2단계(중첩 루프를 레인마다 함께, `x = x + e`·`min`·`max` 모으기)로 roots가 9.13 ms가 되어 기계어 없이 계층(9.57)보다 빠르고, matmul은 914.9에서 50.0 ms다(계층 14.3, 127). 덧붙이기, 점화식, 칸에 더하기, 걷기를 묶어 join이 실행기의 423에서 161 ms가 됐다(계층 74). 남은 값은 덧붙이기 하나 약 100 ns(실행기와 같다), join의 점화식 회차당 약 60 ns, 레인이 적은 중첩 몸체의 해석이고, BFS는 묶이지 않아 기계어를 아직 걷어낼 수 없다(128). 덧붙이기를 리스트의 한 호출로, 점화식을 레지스터 꼴로, 모으기의 끝을 차례 없이 하자 knapsack 10.8, matmul 36.4, join 118, sort 55.5 ms가 됐다(기계어 7.9, 14.2, 72–79, 35.6; 129). BFS는 프런티어 묶음으로 셈하면 차례대로 할 꼬리가 간선의 약 10%다(129). 가드 루프, 묶음 안의 걷기, 커지기만 하는 걸러내기로 BFS도 묶음으로 돌아 93에서 41 ms가 됐다(기계어 7.7, C 10). 간선당 명령 211개 대 38.5개이고, 이 꼴의 바닥(약 50개)이 기계어 위라서 기계어를 걷어내려면 합친 연산이 필요하다(130). 범용 장치만으로(같은 코드의 값 다시 쓰기, 고르기, 비트로 묻기, 아핀 `first + i`) 간선당 112개, 24.0 ms가 됐고, 범용의 바닥은 기계어의 약 2배로 보인다(131). 레지스터 타일 해석기를 시제품으로 재니(133) 산수의 사슬은 C보다 빠르고(roots 5.8 ms) 모으기가 있는 몸체는 졌으며(배낭 4배, BFS 2.4배), 같은 자리에서 꽉 짠 훑기가 BFS 기계어의 1.2배·배낭 0.4배였다: 훑기의 꼴은 옳고 3배는 구현이라, 묶음 프로그램을 로드 때 한 번 만들고 루프를 꽉 짜는 길로 간다. 그 값을 재니 배낭에서 묶음마다 0.90 µs(시간의 38%)이고, 블록의 모양에서 나오는 사실은 로드 때, 값에서 나오는 사실만 묶음마다 보는 설계를 다섯 단계로 세웠다(025). TPC-C −2.8%(기준 3% 안), 은행 질의의 명령 수는 1% 안. 같은 계산이 Pi(JIT)·WSL2·M3 Mac에서 같다. 같은 탐색을 보기로 하면 쓰기는 CPU를 나누는 만큼만 줄고(이벤트로 하면 0.2%로 멈춘다), 본문이 있는 보기는 보기의 스레드(`--view-workers`)가 계산해 연결 스레드를 막지 않는다(쓰기 최대 지연이 CPU만 나눌 때의 1.0배, 연결 스레드 하나에서 묻는 연결 둘이 1.95배) | [018](docs/design/018-lifetimes-and-computation.md), [020](docs/design/020-computing-faster.md), [117](docs/research/117-transient-states.md), [118](docs/research/118-computing-in-one-event.md), [119](docs/research/119-views-that-compute.md), [121](docs/research/121-computing-faster.md), [122](docs/research/122-views-off-the-connection-threads.md), [021](docs/design/021-past-c.md), [123](docs/research/123-past-c.md), [022](docs/design/022-arrays-and-registers.md), [124](docs/research/124-against-c.md), [023](docs/design/023-optimizing-tier.md), [125](docs/research/125-tier-first-steps.md), [024](docs/design/024-batch-executor.md), [126](docs/research/126-batch-executor-stage-one.md), [127](docs/research/127-batch-executor-stage-two.md), [128](docs/research/128-batch-executor-tables-and-costs.md), [129](docs/research/129-batch-executor-costs-cut.md), [130](docs/research/130-batch-executor-frontiers.md), [131](docs/research/131-batch-executor-general-cuts.md), [132](docs/research/132-batch-executor-floor-and-the-way-past.md), [133](docs/research/133-register-tiles-measured.md), [025](docs/design/025-batch-programs.md) |
+| 수명과 계산 | 값의 수명은 선언이 말한다: `state`(저장), `transient state`(앱이 도는 동안: 저장소에 쓰지 않고 다시 시작하면 초기값, SD 카드에서 쓰기 17배), `let`(이벤트 동안). 핸들러와 보기가 계산한다: 이벤트의 리스트(`let seen: list of Seen by node`), 세는 `for i in a .. b`, `break`, 8겹까지의 중첩, 그리고 `view 이름(인자) { … return … }`(스냅숏에서 돌아 소유자를 막지 않는다). 모든 반복은 시작할 때 횟수의 상한을 갖고, 모든 이벤트와 보기는 실행량 예산(정하지 않으면 2^28 명령)과 메모리 몫(2^28 바이트) 안에서 끝난다. 노드 10만·간선 100만의 BFS 한 이벤트가 HTTP로 90 ms였고(처음 397, 020 뒤 191; CPython 452, C 9.1, 123), 022 뒤 코어는 7.4 ms다(같은 프로세스의 C 9.9): 많이 찾는 저장된 리스트를 배열과 칸의 색인으로 펴고, 열을 값만큼 좁히고, 루프 묶음을 레지스터의 기계어로 돈다(리눅스 AArch64의 JIT; 다른 곳은 실행기, 답은 같다). 다만 C를 넘은 것은 미리 읽기 덕이고, 끄면 15.7 ms로 C의 1.57배다. 일곱 가지 일(실수 루프, 배낭, 정렬, 조인, 텍스트, 행렬, BFS)로 C와 견주면 124에서는 3–300배 느렸고, 023의 계층과 표현(HASHED: 차례를 어긴 키는 해시 표의 칸에, 위치가 필요할 때 정렬)으로 지금은 1.19–5.58배(기하 평균 1.92)다(125). Pure의 표현과 뜻대로 손으로 쓴 바닥은 C의 0.90배이고 뜻의 검사는 끌어내면 0–3%라, 막는 것은 실행 계층이다(124). 모든 블록을 SSA로 올리는 최적화 계층(023)의 첫 걸음들이 들어갔고, 텍스트(실행기로 돈다)와 BFS(계층이 덧붙이기를 못 한다)가 남았다. 기계어를 만들지 않는 길로, 루프를 회차 256개의 묶음으로 돌고 연산마다 빌드 때 만든 C 루프를 쓰는 묶음 실행기(024)를 시제품으로 재니 연산을 따로 해석하면 roots 0.99배, 배낭 0.71배(4바이트 레인), BFS 1.03배, join 1.77배, 행렬 곱 2.50배이고, 행렬 곱은 곱해 더하기를 합치면 0.94배였다(계층 1.20–2.46배, 기하 평균 1.92 대 1.58, 합친 꼴로 1.24; 041, 손으로 짠 천장). 1단계(세는 루프의 레인 꼴 몸체, 회차 1024개씩)가 들어가 knapsack이 실행기에서 1,041 ms에서 13.6 ms가 됐다(계층 7.5, 안쪽 루프는 1.17배). 묶음 루프가 없는 모듈의 실행기는 명령과 사이클이 그대로다(126). 2단계(중첩 루프를 레인마다 함께, `x = x + e`·`min`·`max` 모으기)로 roots가 9.13 ms가 되어 기계어 없이 계층(9.57)보다 빠르고, matmul은 914.9에서 50.0 ms다(계층 14.3, 127). 덧붙이기, 점화식, 칸에 더하기, 걷기를 묶어 join이 실행기의 423에서 161 ms가 됐다(계층 74). 남은 값은 덧붙이기 하나 약 100 ns(실행기와 같다), join의 점화식 회차당 약 60 ns, 레인이 적은 중첩 몸체의 해석이고, BFS는 묶이지 않아 기계어를 아직 걷어낼 수 없다(128). 덧붙이기를 리스트의 한 호출로, 점화식을 레지스터 꼴로, 모으기의 끝을 차례 없이 하자 knapsack 10.8, matmul 36.4, join 118, sort 55.5 ms가 됐다(기계어 7.9, 14.2, 72–79, 35.6; 129). BFS는 프런티어 묶음으로 셈하면 차례대로 할 꼬리가 간선의 약 10%다(129). 가드 루프, 묶음 안의 걷기, 커지기만 하는 걸러내기로 BFS도 묶음으로 돌아 93에서 41 ms가 됐다(기계어 7.7, C 10). 간선당 명령 211개 대 38.5개이고, 이 꼴의 바닥(약 50개)이 기계어 위라서 기계어를 걷어내려면 합친 연산이 필요하다(130). 범용 장치만으로(같은 코드의 값 다시 쓰기, 고르기, 비트로 묻기, 아핀 `first + i`) 간선당 112개, 24.0 ms가 됐고, 범용의 바닥은 기계어의 약 2배로 보인다(131). 레지스터 타일 해석기를 시제품으로 재니(133) 산수의 사슬은 C보다 빠르고(roots 5.8 ms) 모으기가 있는 몸체는 졌으며(배낭 4배, BFS 2.4배), 같은 자리에서 꽉 짠 훑기가 BFS 기계어의 1.2배·배낭 0.4배였다: 훑기의 꼴은 옳고 3배는 구현이라, 묶음 프로그램을 로드 때 한 번 만들고 루프를 꽉 짜는 길로 간다. 그 값을 재니 배낭에서 묶음마다 0.90 µs(시간의 38%)이고, 블록의 모양에서 나오는 사실은 로드 때, 값에서 나오는 사실만 묶음마다 보는 설계를 다섯 단계로 세웠다(025). 1단계(블록의 사실을 로드 때 한 번)로 배낭의 묶음마다 값이 0.90에서 0.68 µs, 배낭이 11.48에서 10.34 ms가 됐다. 막대(0.60 µs, 10.3 ms)에는 못 미쳤고, 남은 값은 명령마다의 해석이다(134). TPC-C −2.8%(기준 3% 안), 은행 질의의 명령 수는 1% 안. 같은 계산이 Pi(JIT)·WSL2·M3 Mac에서 같다. 같은 탐색을 보기로 하면 쓰기는 CPU를 나누는 만큼만 줄고(이벤트로 하면 0.2%로 멈춘다), 본문이 있는 보기는 보기의 스레드(`--view-workers`)가 계산해 연결 스레드를 막지 않는다(쓰기 최대 지연이 CPU만 나눌 때의 1.0배, 연결 스레드 하나에서 묻는 연결 둘이 1.95배) | [018](docs/design/018-lifetimes-and-computation.md), [020](docs/design/020-computing-faster.md), [117](docs/research/117-transient-states.md), [118](docs/research/118-computing-in-one-event.md), [119](docs/research/119-views-that-compute.md), [121](docs/research/121-computing-faster.md), [122](docs/research/122-views-off-the-connection-threads.md), [021](docs/design/021-past-c.md), [123](docs/research/123-past-c.md), [022](docs/design/022-arrays-and-registers.md), [124](docs/research/124-against-c.md), [023](docs/design/023-optimizing-tier.md), [125](docs/research/125-tier-first-steps.md), [024](docs/design/024-batch-executor.md), [126](docs/research/126-batch-executor-stage-one.md), [127](docs/research/127-batch-executor-stage-two.md), [128](docs/research/128-batch-executor-tables-and-costs.md), [129](docs/research/129-batch-executor-costs-cut.md), [130](docs/research/130-batch-executor-frontiers.md), [131](docs/research/131-batch-executor-general-cuts.md), [132](docs/research/132-batch-executor-floor-and-the-way-past.md), [133](docs/research/133-register-tiles-measured.md), [025](docs/design/025-batch-programs.md), [134](docs/research/134-batch-plans-stage-one.md) |
| 보기 밀기 | 보기의 `GET`에 `Accept: text/event-stream`이면 그 값을 민다(SSE, 브라우저의 `EventSource` 그대로). 같은 보기와 인자를 보는 구독자는 계산 한 번을 나누고, 값이 바뀔 때만, 많아야 10 ms마다 한 번 보낸다. 느린 구독자에게는 가장 새 값 하나만 기다리게 한다. 모듈이 바뀌어도 같은 이름의 보기로 이어 가고, 토큰을 빼면 끊는다. 구독자 1,000에 쓰기 초당 100번까지 신선도 p99 11 ms 이하, 초당 1번이면 100 ms 폴링의 약 10분의 1 CPU(이벤트 하나와 요청 하나의 값이 같아 메시지 수의 비다), 구독자 1만에 구독자마다 0.3–0.4 KiB, 1만에게 퍼지기까지 p99 45 ms | [019](docs/design/019-pushing-views.md), [120](docs/research/120-pushing-views.md) |
| 영속 | 선언한 상태가 곧 영속 상태다. 목록은 이미지와 로그(저장마다 바뀐 기록 한 항목, 재사용하는 할당 구간, 앞 항목의 다이제스트로 이어짐)에, 나머지는 트리 저장소에(커밋당 한 연속 구간과 동기화 한 번). 이벤트 묶음마다 저장 한 번, 이미지는 소유자 밖에서 체크포인트. `kill -9`와 커밋 각 단계의 죽음 뒤 확인 응답한 이벤트가 모두 남는다. 레코드·키가 바뀐 모듈로 바꿔도 데이터가 이름으로 옮겨진다 | [076](docs/research/076-one-sync-commit.md), [081](docs/research/081-tail-records.md), [092](docs/research/092-tpcc.md), 008 상태 영속·교체 |
| 인터페이스 | 한 명세가 TUI(셀 단위 갱신), CLI, 문장(NUI), 웹 GUI가 된다. 저장하면 실행 중인 앱이 상태를 이어 새 모듈로 바뀐다(`aup watch`, `vega … --watch`) | Vega `docs/design/001-first-component.md`, Vega `make check`(가상 터미널, 헤드리스 Chromium 포함) |
diff --git a/docs/design/025-batch-programs.md b/docs/design/025-batch-programs.md
index ce8fe6f..6f2c7fe 100644
--- a/docs/design/025-batch-programs.md
+++ b/docs/design/025-batch-programs.md
@@ -1,6 +1,7 @@
# 025 — 묶음 프로그램: 블록은 로드 때 한 번 읽고, 묶음마다는 값만 본다
-작성일: 2026-10-08. 상태: 설계. [024](024-batch-executor.md)가 처음에 적고 만들지 않은 단계("묶음 프로그램은 루프마다 한 번
+작성일: 2026-10-08. 상태: 1단계를 만들었다([134](../research/134-batch-plans-stage-one.md): 배낭의 묶음마다 0.90 → 0.68 µs, 11.48 →
+10.34 ms. 막대 ≤ 0.60 µs와 ≤ 10.3 ms에 못 미쳤다. 남은 것은 명령마다 `Val`을 다루는 해석으로, 2단계의 몫이다). [024](024-batch-executor.md)가 처음에 적고 만들지 않은 단계("묶음 프로그램은 루프마다 한 번
만든다")이고, [132](../research/132-batch-executor-floor-and-the-way-past.md)의 A, [133](../research/133-register-tiles-measured.md)이
정한 길이다. 사용자의 방향: 범용 장치로, 합친 연산의 표 없이, 기계어 없이, 메모리를 적게(2026-10-05, 2026-10-08).
diff --git a/docs/research/134-batch-plans-stage-one.md b/docs/research/134-batch-plans-stage-one.md
new file mode 100644
index 0000000..9426a65
--- /dev/null
+++ b/docs/research/134-batch-plans-stage-one.md
@@ -0,0 +1,72 @@
+# 134 — 묶음 프로그램 1단계: 블록의 사실을 로드 때 한 번
+
+2026-10-08. [025](../design/025-batch-programs.md)의 1단계다. 묶음 루프마다 블록을 로드 때 한 번 읽어 계획을 만들고, 묶음은
+그 계획을 본다. 코드는 `c112238`(쓰는 지역 변수, 항목, 칸에 더하기, 명령마다의 몫, 모으기의 앞)과 `140cc46`(점화식)이다.
+잰 것은 `b10` = `140cc46`을 `b9` = `735ba81`과 번갈아 돈 것이다(`041-batches/runs.sh pairs`, 11번의 중앙값을 다섯 번씩, 그 중앙값).
+
+## 쉬운 말로
+
+- **막대에 못 미쳤다.** 배낭의 묶음마다 값이 0.90에서 0.68 µs로 4분의 1 줄었다(막대 ≤ 0.60). 배낭은 11.48에서 10.34 ms다(막대
+ ≤ 10.3). sum0의 묶음마다 명령은 2,330에서 1,822다(막대 ≤ 1,550). 025는 블록 다시 읽기를 묶음마다의 3분의 1 남짓으로 셈했는데,
+ 그 셈이 `pass`의 명령마다 머리(`arrive`, 고르기의 끝, 레인이 없는지)를 "찾기"에 넣었다. 그것들은 그대로 남는다.
+- **남은 0.68 µs는 2단계의 몫이다.** 명령마다 `Val`을 쌓고(지역 변수, 필드, 행), 값이 정수인지 묻고(`whole`), 경계를 보고(`rows_in`),
+ 읽기를 기록하고(`read_of`), 키의 첫 행을 찾는다(`find`). 블록을 다시 읽는 일은 거의 다 없어졌다.
+- **다른 일은 그대로다.** matmul은 40.0에서 38.2 ms가 됐지만 명령 수가 같다(놓인 자리). BFS는 24.04에서 24.31 ms인데 명령은
+ 0.2% 적다(놓인 자리). join, sort, words는 같다. 실행기만의 길은 명령 수가 그대로다.
+
+## 잰 것
+
+| 일 | `b9` | `b10` | 레인 256 (`b10`) | 명령 (`b9` → `b10`, 프로세스 전체) |
+|---|---:|---:|---:|---:|
+| knapsack | 11.48 ms | **10.34** | 20.24 (`b9` 24.56) | −4.3% |
+| matmul | 40.01 | 38.19 | 38.27 | −0.04% |
+| roots | 8.97 | 8.81 | 10.45 | −1.7% |
+| join | 116.95 | 117.04 | 118.78 | −1.3% |
+| BFS | 24.04 | 24.31 | 25.66 | −0.2% |
+| sort | 57.30 | 56.95 | | |
+| words | 215.8 | 218.8 | | |
+
+- **배낭의 묶음마다**: (20.24 − 10.34) / 14,490 묶음 = 0.68 µs(`b9` 0.90).
+- **sum0과 ops0의 묶음마다**(`icount`, 세 번의 3,000과 12,000 묶음): sum0 2,330 → 1,822 명령, 805 → 597 사이클; ops0 2,103 → 1,658
+ 명령, 717 → 521 사이클.
+- **sum0의 시간은 8.69에서 6.13 ms다.** 그러나 대부분은 1단계의 몫이 아니다. 모으기의 부분합을 놓는 루프(`reduction`)를 `b9`의
+ 컴파일러는 레인 하나씩 스칼라로 짰고(요소당 명령 5), `b10`에서는 같은 소스를 넷씩 벡터로 짰다. 묶음 구조체가 바뀌어 인라인의
+ 자리가 달라진 값이다. 3단계(모으기를 꼴에 맞게)에서 이 루프 자체를 없애거나 늘 벡터가 되게 한다.
+- 실행기만의 길(`PURE_BATCH=0`)은 다섯 일 모두 명령 수가 같다. 사이클은 BFS +3.9%, join +1.3%로 오갔다(같은 명령: 놓인 자리).
+
+원본: `041-batches/2026-10-08-pairs.jsonl`, `2026-10-08-counts.jsonl`, `2026-10-08-batch-costs.jsonl`.
+
+## 만든 것
+
+- **계획**(`PureBatchPlan`, `module-batch.c`). 모듈을 다 읽은 뒤(명령의 계획 비트가 모두 정해진 뒤) 묶음 루프마다 하나. 쓰는 지역
+ 변수와 항목, 칸에 더하기, 점화식(갱신, 걸음, 기록, 한 회차의 동작, 남는 수의 자리), 블록의 명령마다 몫(칸에 더하기의 again·add·set,
+ 점화식의 갱신·기록, 모으기의 앞까지의 거리). 모듈은 루프의 차례로 계획을 쥐고, 묶음은 이분 탐색으로 찾는다. 점화식이 없는 루프의
+ 계획은 블록 길이 × 4 바이트에 수백 바이트다.
+- **남는 수는 출처로.** 점화식의 상수·필드·LOAD·블록이 쓰지 않는 지역 변수는 출처마다 한 자리이고, 묶음을 열 때 값을 채운다. 전에는
+ 값의 비트로 자리를 나눴다(값이 같으면 한 자리). 두 출처의 값이 우연히 같을 때만 다르고, 답은 같다.
+- **견줌**(`PURE_BATCH_CHECK`, 빌드 플래그). 만드는 동안 묶음마다 블록을 옛 방식으로 다시 읽어 계획과 견주고, 다르면 멈추게 했다.
+ 무작위 시드와 고정 소스, 단위 시험이 이 견줌을 지난 뒤 지웠다.
+- **레인 수 플래그**(`PURE_BATCH_LANES`). 묶음마다의 값을 1024와 256의 차로 재려고, 소스를 고치지 않고 빌드한다.
+- `041-batches/runs.sh pairs`: 두 빌드를 번갈아 잰다.
+
+## 남은 묶음마다 (`b10`, 배낭, 레인 256의 덧값)
+
+| 무엇 | 줄 |
+|---|---|
+| 지역 변수를 쌓기(`local_load`의 push), 필드·행을 균일 값으로 쌓기 | 1246, 2007, 2052 |
+| 정수인지 묻기(`whole`, `whole32`), 경계(`rows_in`) | 228, 229, 628 |
+| 4바이트 연산의 준비(`word_op`), 지역 변수 쓰기(`local_store`) | 499, 507, 919 |
+| 읽기의 기록(`read_of`, `reads_set`), 키의 첫 행(`find`), 칸(`cells_at`) | 718, 719, 2444, 759, 813, 656 |
+| 명령마다의 머리와 디스패치(`switch`, 몫, 같은 코드의 값) | 1964–1987, 2001 |
+| 미룬 쓰기의 확인(`settle`) | 2565 |
+
+명령 하나에 약 80 사이클이다. 2단계는 이것을 정적 꼴(균일·아핀은 스칼라 걸음), 묶음에 한 번 가져오는 리스트의 사실, 묶음을 열 때
+고르는 커널로 줄인다.
+
+## 뜻과 맞음
+
+- 견줌 빌드에서 시드 1, 2, 3, 5, 23의 무작위 프로그램(기본과 `PURE_SELECT=all`), 고정 소스 여섯의 세 길(두 모드), 단위 시험 세 모드가
+ 지났다. 계획이 옛 셈과 어긋난 일은 없었다.
+- 견줌을 지운 빌드에서 단위 시험 세 모드, ASan 단위 시험(기본과 실행기 모드), sanitized `pure-run`의 고정 소스(두 모드)와 시드 23의
+ 프로그램(두 모드), Pure의 `make check`, aup의 검사 두 모드가 지났다.
+- TPC-C와 은행 질의는 재지 않았다. 실행기(`run`)는 건드리지 않았고, 묶음 루프가 없는 길의 명령 수가 같음을 위에서 셌다.
diff --git a/research/experiments/041-batches/2026-10-08-batch-costs.jsonl b/research/experiments/041-batches/2026-10-08-batch-costs.jsonl
index b3eba50..49332e9 100644
--- a/research/experiments/041-batches/2026-10-08-batch-costs.jsonl
+++ b/research/experiments/041-batches/2026-10-08-batch-costs.jsonl
@@ -83,3 +83,9 @@
{"work": "knapsack", "line": "module-batch.c:1715", "ms_1024": 0.042, "ms_256": 0.103}
{"note":"time.c sum0 n=200000 (4M rounds), 21 rounds median, b9 BATCH 1024 then 256","module":"sum0.pmod","n":200000,"ms":8.633,"work":44000019,"out":4000000}
{"note":"time.c sum0 n=200000 (4M rounds), 21 rounds median, b9 BATCH 1024 then 256","module":"sum0.pmod","n":200000,"ms":12.544,"work":44000019,"out":4000000}
+{"note":"icount over time.c's 3 rounds (n 51200: 3,000 and 12,000 batches), b9 = 735ba81 and b10 = 140cc46 (design 025 stage 1), BATCH 1024 and 256","version":"b9","module":"sum0","instructions":[47885467,68855349],"cycles":[16221217,23496974]}
+{"version":"b9","module":"ops0","instructions":[66412005,85341885],"cycles":[30351488,36805399]}
+{"version":"b10","module":"sum0","instructions":[34047758,50448573],"cycles":[11650551,17023859]}
+{"version":"b10","module":"ops0","instructions":[52772240,67691004],"cycles":[25970653,30661478]}
+{"note":"time.c sum0 n=200000, 21 rounds median, twice each","version":"b9","ms":[8.690,8.689],"ms_256":[12.545,12.552]}
+{"version":"b10","ms":[6.133,6.136],"ms_256":[9.069,9.070]}
diff --git a/research/experiments/041-batches/2026-10-08-counts.jsonl b/research/experiments/041-batches/2026-10-08-counts.jsonl
index abd3cdc..546030b 100644
--- a/research/experiments/041-batches/2026-10-08-counts.jsonl
+++ b/research/experiments/041-batches/2026-10-08-counts.jsonl
@@ -26,3 +26,13 @@
{"version":"b9","way":"rounds","work":"bfs","instructions":4711633461,"cycles":2071776932}
{"version":"b9","way":"batches","work":"words","instructions":3977119553,"cycles":1807496057}
{"version":"b9","way":"rounds","work":"words","instructions":3934435031,"cycles":1790741535}
+{"version":"b10","way":"batches","work":"bfs","instructions":3366842980,"cycles":1652810363}
+{"version":"b10","way":"rounds","work":"bfs","instructions":4711632737,"cycles":2152941471}
+{"version":"b10","way":"batches","work":"knapsack","instructions":377446216,"cycles":119812731}
+{"version":"b10","way":"rounds","work":"knapsack","instructions":21688103096,"cycles":7553046280}
+{"version":"b10","way":"batches","work":"matmul","instructions":967871186,"cycles":329990340}
+{"version":"b10","way":"rounds","work":"matmul","instructions":21436715982,"cycles":6620761799}
+{"version":"b10","way":"batches","work":"roots","instructions":165861326,"cycles":113397325}
+{"version":"b10","way":"rounds","work":"roots","instructions":2755945365,"cycles":989112526}
+{"version":"b10","way":"batches","work":"join","instructions":1868533943,"cycles":1049050190}
+{"version":"b10","way":"rounds","work":"join","instructions":7669661641,"cycles":3314577656}
diff --git a/research/experiments/041-batches/2026-10-08-pairs.jsonl b/research/experiments/041-batches/2026-10-08-pairs.jsonl
new file mode 100644
index 0000000..1d16abf
--- /dev/null
+++ b/research/experiments/041-batches/2026-10-08-pairs.jsonl
@@ -0,0 +1,120 @@
+{"version":"b9","way":"batches","turn":1,"work":"roots","pure_median_ms":8.980,"c_median_ms":6.869,"ratio":1.307,"agree":true}
+{"version":"b9","way":"batches","turn":1,"work":"knapsack","pure_median_ms":11.394,"c_median_ms":6.228,"ratio":1.829,"agree":true}
+{"version":"b9","way":"batches","turn":1,"work":"sort","pure_median_ms":57.568,"c_median_ms":26.864,"ratio":2.143,"agree":true}
+{"version":"b9","way":"batches","turn":1,"work":"join","pure_median_ms":116.419,"c_median_ms":30.414,"ratio":3.828,"agree":true}
+{"version":"b9","way":"batches","turn":1,"work":"words","pure_median_ms":214.520,"c_median_ms":38.595,"ratio":5.558,"agree":true}
+{"version":"b9","way":"batches","turn":1,"work":"matmul","pure_median_ms":40.297,"c_median_ms":7.797,"ratio":5.168,"agree":true}
+{"version":"b9","way":"batches","turn":1,"work":"bfs","pure_median_ms":24.040,"c_median_ms":10.044,"ratio":2.394,"agree":true}
+{"version":"b10","way":"batches","turn":1,"work":"roots","pure_median_ms":8.807,"c_median_ms":6.864,"ratio":1.283,"agree":true}
+{"version":"b10","way":"batches","turn":1,"work":"knapsack","pure_median_ms":10.333,"c_median_ms":6.227,"ratio":1.659,"agree":true}
+{"version":"b10","way":"batches","turn":1,"work":"sort","pure_median_ms":57.052,"c_median_ms":26.991,"ratio":2.114,"agree":true}
+{"version":"b10","way":"batches","turn":1,"work":"join","pure_median_ms":117.635,"c_median_ms":31.922,"ratio":3.685,"agree":true}
+{"version":"b10","way":"batches","turn":1,"work":"words","pure_median_ms":219.952,"c_median_ms":38.134,"ratio":5.768,"agree":true}
+{"version":"b10","way":"batches","turn":1,"work":"matmul","pure_median_ms":38.223,"c_median_ms":7.648,"ratio":4.998,"agree":true}
+{"version":"b10","way":"batches","turn":1,"work":"bfs","pure_median_ms":24.013,"c_median_ms":10.000,"ratio":2.401,"agree":true}
+{"version":"b9","way":"batches","turn":2,"work":"roots","pure_median_ms":8.968,"c_median_ms":6.864,"ratio":1.306,"agree":true}
+{"version":"b9","way":"batches","turn":2,"work":"knapsack","pure_median_ms":11.492,"c_median_ms":6.241,"ratio":1.841,"agree":true}
+{"version":"b9","way":"batches","turn":2,"work":"sort","pure_median_ms":57.300,"c_median_ms":26.909,"ratio":2.129,"agree":true}
+{"version":"b9","way":"batches","turn":2,"work":"join","pure_median_ms":119.748,"c_median_ms":31.512,"ratio":3.800,"agree":true}
+{"version":"b9","way":"batches","turn":2,"work":"words","pure_median_ms":217.985,"c_median_ms":38.971,"ratio":5.594,"agree":true}
+{"version":"b9","way":"batches","turn":2,"work":"matmul","pure_median_ms":39.961,"c_median_ms":7.564,"ratio":5.283,"agree":true}
+{"version":"b9","way":"batches","turn":2,"work":"bfs","pure_median_ms":24.185,"c_median_ms":9.994,"ratio":2.420,"agree":true}
+{"version":"b10","way":"batches","turn":2,"work":"roots","pure_median_ms":8.806,"c_median_ms":6.860,"ratio":1.284,"agree":true}
+{"version":"b10","way":"batches","turn":2,"work":"knapsack","pure_median_ms":10.353,"c_median_ms":6.234,"ratio":1.661,"agree":true}
+{"version":"b10","way":"batches","turn":2,"work":"sort","pure_median_ms":57.228,"c_median_ms":26.991,"ratio":2.120,"agree":true}
+{"version":"b10","way":"batches","turn":2,"work":"join","pure_median_ms":118.019,"c_median_ms":30.951,"ratio":3.813,"agree":true}
+{"version":"b10","way":"batches","turn":2,"work":"words","pure_median_ms":218.773,"c_median_ms":38.899,"ratio":5.624,"agree":true}
+{"version":"b10","way":"batches","turn":2,"work":"matmul","pure_median_ms":38.100,"c_median_ms":7.532,"ratio":5.059,"agree":true}
+{"version":"b10","way":"batches","turn":2,"work":"bfs","pure_median_ms":25.120,"c_median_ms":9.977,"ratio":2.518,"agree":true}
+{"version":"b9","way":"batches","turn":3,"work":"roots","pure_median_ms":8.974,"c_median_ms":6.865,"ratio":1.307,"agree":true}
+{"version":"b9","way":"batches","turn":3,"work":"knapsack","pure_median_ms":11.506,"c_median_ms":6.232,"ratio":1.846,"agree":true}
+{"version":"b9","way":"batches","turn":3,"work":"sort","pure_median_ms":57.332,"c_median_ms":26.889,"ratio":2.132,"agree":true}
+{"version":"b9","way":"batches","turn":3,"work":"join","pure_median_ms":117.531,"c_median_ms":28.974,"ratio":4.056,"agree":true}
+{"version":"b9","way":"batches","turn":3,"work":"words","pure_median_ms":215.820,"c_median_ms":38.800,"ratio":5.562,"agree":true}
+{"version":"b9","way":"batches","turn":3,"work":"matmul","pure_median_ms":40.006,"c_median_ms":7.569,"ratio":5.286,"agree":true}
+{"version":"b9","way":"batches","turn":3,"work":"bfs","pure_median_ms":23.877,"c_median_ms":9.995,"ratio":2.389,"agree":true}
+{"version":"b10","way":"batches","turn":3,"work":"roots","pure_median_ms":8.814,"c_median_ms":6.866,"ratio":1.284,"agree":true}
+{"version":"b10","way":"batches","turn":3,"work":"knapsack","pure_median_ms":10.335,"c_median_ms":6.298,"ratio":1.641,"agree":true}
+{"version":"b10","way":"batches","turn":3,"work":"sort","pure_median_ms":56.946,"c_median_ms":27.016,"ratio":2.108,"agree":true}
+{"version":"b10","way":"batches","turn":3,"work":"join","pure_median_ms":116.656,"c_median_ms":30.993,"ratio":3.764,"agree":true}
+{"version":"b10","way":"batches","turn":3,"work":"words","pure_median_ms":217.339,"c_median_ms":38.694,"ratio":5.617,"agree":true}
+{"version":"b10","way":"batches","turn":3,"work":"matmul","pure_median_ms":38.145,"c_median_ms":7.531,"ratio":5.065,"agree":true}
+{"version":"b10","way":"batches","turn":3,"work":"bfs","pure_median_ms":24.079,"c_median_ms":10.207,"ratio":2.359,"agree":true}
+{"version":"b9","way":"batches","turn":4,"work":"roots","pure_median_ms":8.966,"c_median_ms":6.866,"ratio":1.306,"agree":true}
+{"version":"b9","way":"batches","turn":4,"work":"knapsack","pure_median_ms":11.481,"c_median_ms":6.282,"ratio":1.828,"agree":true}
+{"version":"b9","way":"batches","turn":4,"work":"sort","pure_median_ms":57.021,"c_median_ms":26.909,"ratio":2.119,"agree":true}
+{"version":"b9","way":"batches","turn":4,"work":"join","pure_median_ms":116.953,"c_median_ms":30.935,"ratio":3.781,"agree":true}
+{"version":"b9","way":"batches","turn":4,"work":"words","pure_median_ms":214.897,"c_median_ms":38.408,"ratio":5.595,"agree":true}
+{"version":"b9","way":"batches","turn":4,"work":"matmul","pure_median_ms":39.965,"c_median_ms":7.549,"ratio":5.294,"agree":true}
+{"version":"b9","way":"batches","turn":4,"work":"bfs","pure_median_ms":23.998,"c_median_ms":10.048,"ratio":2.388,"agree":true}
+{"version":"b10","way":"batches","turn":4,"work":"roots","pure_median_ms":8.817,"c_median_ms":6.863,"ratio":1.285,"agree":true}
+{"version":"b10","way":"batches","turn":4,"work":"knapsack","pure_median_ms":10.351,"c_median_ms":6.228,"ratio":1.662,"agree":true}
+{"version":"b10","way":"batches","turn":4,"work":"sort","pure_median_ms":56.635,"c_median_ms":26.988,"ratio":2.098,"agree":true}
+{"version":"b10","way":"batches","turn":4,"work":"join","pure_median_ms":116.859,"c_median_ms":32.928,"ratio":3.549,"agree":true}
+{"version":"b10","way":"batches","turn":4,"work":"words","pure_median_ms":218.361,"c_median_ms":38.490,"ratio":5.673,"agree":true}
+{"version":"b10","way":"batches","turn":4,"work":"matmul","pure_median_ms":38.201,"c_median_ms":7.556,"ratio":5.056,"agree":true}
+{"version":"b10","way":"batches","turn":4,"work":"bfs","pure_median_ms":24.158,"c_median_ms":10.084,"ratio":2.396,"agree":true}
+{"version":"b9","way":"batches","turn":5,"work":"roots","pure_median_ms":8.966,"c_median_ms":6.865,"ratio":1.306,"agree":true}
+{"version":"b9","way":"batches","turn":5,"work":"knapsack","pure_median_ms":11.425,"c_median_ms":6.233,"ratio":1.833,"agree":true}
+{"version":"b9","way":"batches","turn":5,"work":"sort","pure_median_ms":56.915,"c_median_ms":26.897,"ratio":2.116,"agree":true}
+{"version":"b9","way":"batches","turn":5,"work":"join","pure_median_ms":116.403,"c_median_ms":32.129,"ratio":3.623,"agree":true}
+{"version":"b9","way":"batches","turn":5,"work":"words","pure_median_ms":216.542,"c_median_ms":38.769,"ratio":5.585,"agree":true}
+{"version":"b9","way":"batches","turn":5,"work":"matmul","pure_median_ms":40.014,"c_median_ms":7.525,"ratio":5.318,"agree":true}
+{"version":"b9","way":"batches","turn":5,"work":"bfs","pure_median_ms":24.262,"c_median_ms":9.986,"ratio":2.430,"agree":true}
+{"version":"b10","way":"batches","turn":5,"work":"roots","pure_median_ms":8.805,"c_median_ms":6.862,"ratio":1.283,"agree":true}
+{"version":"b10","way":"batches","turn":5,"work":"knapsack","pure_median_ms":10.360,"c_median_ms":6.237,"ratio":1.661,"agree":true}
+{"version":"b10","way":"batches","turn":5,"work":"sort","pure_median_ms":56.372,"c_median_ms":26.977,"ratio":2.090,"agree":true}
+{"version":"b10","way":"batches","turn":5,"work":"join","pure_median_ms":119.324,"c_median_ms":31.176,"ratio":3.827,"agree":true}
+{"version":"b10","way":"batches","turn":5,"work":"words","pure_median_ms":220.252,"c_median_ms":38.583,"ratio":5.709,"agree":true}
+{"version":"b10","way":"batches","turn":5,"work":"matmul","pure_median_ms":38.180,"c_median_ms":7.518,"ratio":5.078,"agree":true}
+{"version":"b10","way":"batches","turn":5,"work":"bfs","pure_median_ms":24.372,"c_median_ms":9.977,"ratio":2.443,"agree":true}
+{"version":"b10","way":"batches","turn":1,"work":"roots","pure_median_ms":8.817,"c_median_ms":6.862,"ratio":1.285,"agree":true}
+{"version":"b10","way":"batches","turn":1,"work":"knapsack","pure_median_ms":10.376,"c_median_ms":6.229,"ratio":1.666,"agree":true}
+{"version":"b10","way":"batches","turn":1,"work":"join","pure_median_ms":116.358,"c_median_ms":31.481,"ratio":3.696,"agree":true}
+{"version":"b10","way":"batches","turn":1,"work":"matmul","pure_median_ms":38.211,"c_median_ms":7.512,"ratio":5.087,"agree":true}
+{"version":"b10","way":"batches","turn":1,"work":"bfs","pure_median_ms":24.461,"c_median_ms":9.892,"ratio":2.473,"agree":true}
+{"version":"b10-256","way":"batches","turn":1,"work":"roots","pure_median_ms":10.451,"c_median_ms":6.864,"ratio":1.523,"agree":true}
+{"version":"b10-256","way":"batches","turn":1,"work":"knapsack","pure_median_ms":20.236,"c_median_ms":6.235,"ratio":3.246,"agree":true}
+{"version":"b10-256","way":"batches","turn":1,"work":"join","pure_median_ms":118.859,"c_median_ms":34.245,"ratio":3.471,"agree":true}
+{"version":"b10-256","way":"batches","turn":1,"work":"matmul","pure_median_ms":38.290,"c_median_ms":7.574,"ratio":5.055,"agree":true}
+{"version":"b10-256","way":"batches","turn":1,"work":"bfs","pure_median_ms":25.834,"c_median_ms":9.914,"ratio":2.606,"agree":true}
+{"version":"b10","way":"batches","turn":2,"work":"roots","pure_median_ms":8.819,"c_median_ms":6.872,"ratio":1.283,"agree":true}
+{"version":"b10","way":"batches","turn":2,"work":"knapsack","pure_median_ms":10.330,"c_median_ms":6.225,"ratio":1.659,"agree":true}
+{"version":"b10","way":"batches","turn":2,"work":"join","pure_median_ms":118.732,"c_median_ms":31.807,"ratio":3.733,"agree":true}
+{"version":"b10","way":"batches","turn":2,"work":"matmul","pure_median_ms":38.275,"c_median_ms":7.565,"ratio":5.059,"agree":true}
+{"version":"b10","way":"batches","turn":2,"work":"bfs","pure_median_ms":24.444,"c_median_ms":9.979,"ratio":2.450,"agree":true}
+{"version":"b10-256","way":"batches","turn":2,"work":"roots","pure_median_ms":10.449,"c_median_ms":6.863,"ratio":1.523,"agree":true}
+{"version":"b10-256","way":"batches","turn":2,"work":"knapsack","pure_median_ms":20.235,"c_median_ms":6.229,"ratio":3.248,"agree":true}
+{"version":"b10-256","way":"batches","turn":2,"work":"join","pure_median_ms":119.501,"c_median_ms":32.403,"ratio":3.688,"agree":true}
+{"version":"b10-256","way":"batches","turn":2,"work":"matmul","pure_median_ms":38.186,"c_median_ms":7.613,"ratio":5.016,"agree":true}
+{"version":"b10-256","way":"batches","turn":2,"work":"bfs","pure_median_ms":25.919,"c_median_ms":10.025,"ratio":2.586,"agree":true}
+{"version":"b10","way":"batches","turn":3,"work":"roots","pure_median_ms":9.207,"c_median_ms":6.954,"ratio":1.324,"agree":true}
+{"version":"b10","way":"batches","turn":3,"work":"knapsack","pure_median_ms":10.327,"c_median_ms":6.236,"ratio":1.656,"agree":true}
+{"version":"b10","way":"batches","turn":3,"work":"join","pure_median_ms":115.733,"c_median_ms":30.953,"ratio":3.739,"agree":true}
+{"version":"b10","way":"batches","turn":3,"work":"matmul","pure_median_ms":38.172,"c_median_ms":7.569,"ratio":5.043,"agree":true}
+{"version":"b10","way":"batches","turn":3,"work":"bfs","pure_median_ms":24.941,"c_median_ms":9.942,"ratio":2.509,"agree":true}
+{"version":"b10-256","way":"batches","turn":3,"work":"roots","pure_median_ms":10.446,"c_median_ms":6.861,"ratio":1.522,"agree":true}
+{"version":"b10-256","way":"batches","turn":3,"work":"knapsack","pure_median_ms":20.243,"c_median_ms":6.231,"ratio":3.249,"agree":true}
+{"version":"b10-256","way":"batches","turn":3,"work":"join","pure_median_ms":118.431,"c_median_ms":30.618,"ratio":3.868,"agree":true}
+{"version":"b10-256","way":"batches","turn":3,"work":"matmul","pure_median_ms":38.266,"c_median_ms":7.710,"ratio":4.963,"agree":true}
+{"version":"b10-256","way":"batches","turn":3,"work":"bfs","pure_median_ms":25.650,"c_median_ms":9.951,"ratio":2.578,"agree":true}
+{"version":"b10","way":"batches","turn":4,"work":"roots","pure_median_ms":8.815,"c_median_ms":6.860,"ratio":1.285,"agree":true}
+{"version":"b10","way":"batches","turn":4,"work":"knapsack","pure_median_ms":10.336,"c_median_ms":6.226,"ratio":1.660,"agree":true}
+{"version":"b10","way":"batches","turn":4,"work":"join","pure_median_ms":116.960,"c_median_ms":30.961,"ratio":3.778,"agree":true}
+{"version":"b10","way":"batches","turn":4,"work":"matmul","pure_median_ms":38.255,"c_median_ms":7.588,"ratio":5.042,"agree":true}
+{"version":"b10","way":"batches","turn":4,"work":"bfs","pure_median_ms":24.069,"c_median_ms":10.014,"ratio":2.404,"agree":true}
+{"version":"b10-256","way":"batches","turn":4,"work":"roots","pure_median_ms":10.456,"c_median_ms":6.865,"ratio":1.523,"agree":true}
+{"version":"b10-256","way":"batches","turn":4,"work":"knapsack","pure_median_ms":20.335,"c_median_ms":6.230,"ratio":3.264,"agree":true}
+{"version":"b10-256","way":"batches","turn":4,"work":"join","pure_median_ms":118.782,"c_median_ms":32.616,"ratio":3.642,"agree":true}
+{"version":"b10-256","way":"batches","turn":4,"work":"matmul","pure_median_ms":38.260,"c_median_ms":7.581,"ratio":5.047,"agree":true}
+{"version":"b10-256","way":"batches","turn":4,"work":"bfs","pure_median_ms":25.665,"c_median_ms":9.941,"ratio":2.582,"agree":true}
+{"version":"b10","way":"batches","turn":5,"work":"roots","pure_median_ms":8.803,"c_median_ms":6.864,"ratio":1.283,"agree":true}
+{"version":"b10","way":"batches","turn":5,"work":"knapsack","pure_median_ms":10.344,"c_median_ms":6.238,"ratio":1.658,"agree":true}
+{"version":"b10","way":"batches","turn":5,"work":"join","pure_median_ms":117.119,"c_median_ms":34.635,"ratio":3.382,"agree":true}
+{"version":"b10","way":"batches","turn":5,"work":"matmul","pure_median_ms":38.172,"c_median_ms":7.580,"ratio":5.036,"agree":true}
+{"version":"b10","way":"batches","turn":5,"work":"bfs","pure_median_ms":24.251,"c_median_ms":9.960,"ratio":2.435,"agree":true}
+{"version":"b10-256","way":"batches","turn":5,"work":"roots","pure_median_ms":10.443,"c_median_ms":6.873,"ratio":1.519,"agree":true}
+{"version":"b10-256","way":"batches","turn":5,"work":"knapsack","pure_median_ms":20.293,"c_median_ms":6.237,"ratio":3.254,"agree":true}
+{"version":"b10-256","way":"batches","turn":5,"work":"join","pure_median_ms":118.650,"c_median_ms":31.727,"ratio":3.740,"agree":true}
+{"version":"b10-256","way":"batches","turn":5,"work":"matmul","pure_median_ms":38.295,"c_median_ms":7.578,"ratio":5.053,"agree":true}
+{"version":"b10-256","way":"batches","turn":5,"work":"bfs","pure_median_ms":25.631,"c_median_ms":10.087,"ratio":2.541,"agree":true}
diff --git a/research/experiments/041-batches/runs.sh b/research/experiments/041-batches/runs.sh
index 876f249..4da74fb 100755
--- a/research/experiments/041-batches/runs.sh
+++ b/research/experiments/041-batches/runs.sh
@@ -10,6 +10,11 @@
# runs.sh counts NAME [WORK ...] user-mode instructions and cycles
# of suite-run's 3 rounds, each of
# the two executor ways (icount.c)
+# runs.sh pairs A B ROUNDS TIMES [WORK ...] the batches way of A then B,
+# TIMES turns (interleaved: the
+# medians of A's and B's turns)
+# (A build of another lane count: CPPFLAGS=-DPURE_BATCH_LANES=256 with
+# 040's runs.sh build.)
set -e
here=research/experiments/041-batches
builds=$HOME/.cache/pure-040
@@ -41,7 +46,17 @@ counts)
| sed "s/^{/{\"version\":\"$name\",\"way\":\"$way\",\"work\":\"$work\",/" >> "$here/$day-counts.jsonl"
done
done ;;
-*) echo "runs.sh suite NAME ROUNDS [WORK ...] | counts NAME [WORK ...]" >&2
+pairs)
+ other=$1 rounds=$2 times=$3
+ shift 3
+ for turn in $(seq "$times"); do
+ for version in "$name" "$other"; do
+ quiet
+ PURE_JIT=0 python3 research/guard.py --most 3072 -- "$builds/$version/suite-run" "$builds/$version" "$rounds" "$@" \
+ | grep median | sed "s/^{/{\"version\":\"$version\",\"way\":\"batches\",\"turn\":$turn,/" >> "$here/$day-pairs.jsonl"
+ done
+ done ;;
+*) echo "runs.sh suite NAME ROUNDS [WORK ...] | counts NAME [WORK ...] | pairs A B ROUNDS TIMES [WORK ...]" >&2
exit 2 ;;
esac
# 2026-10-08-lanes.jsonl (research 132): b9's code with BATCH 1024, 512 and