aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/design/024-batch-executor.md2
-rw-r--r--docs/design/025-batch-programs.md9
-rw-r--r--docs/research/138-batch-executor-stage-five.md79
3 files changed, 86 insertions, 4 deletions
diff --git a/docs/design/024-batch-executor.md b/docs/design/024-batch-executor.md
index 0605c4d..a0f9ce0 100644
--- a/docs/design/024-batch-executor.md
+++ b/docs/design/024-batch-executor.md
@@ -1,6 +1,6 @@
# 024 — 묶음 실행기: 기계어 없이 루프를 C 가까이
-작성일: 2026-10-05. 상태: 제안과 시제품([041](../../research/experiments/041-batches/)). 1단계를 만들었다([126](../research/126-batch-executor-stage-one.md): knapsack이 실행기에서 1,041 ms → 13.6 ms, 안쪽 루프는 계층의 1.17배로 기준 미달). 2단계(중첩 루프를 함께, 모으기)를 만들었다([127](../research/127-batch-executor-stage-two.md): roots 9.13 ms로 계층 9.57보다 빠름, matmul 50 ms). 4단계의 앞 절반(덧붙이기), 5단계(점화식), 칸에 더하기, 걷기를 만들었다([128](../research/128-batch-executor-tables-and-costs.md): join이 실행기의 423에서 161 ms, 계층 74). 남은 값은 덧붙이기 하나 약 100 ns, join의 점화식 회차당 약 60 ns, 레인이 적은 중첩 몸체의 해석이고, BFS는 아직 묶이지 않는다(바뀌는 개수, 커지기만 하는 걸러내기, 중첩 걷기가 필요하다). 그 셋을 줄였다([129](../research/129-batch-executor-costs-cut.md): 한꺼번에 덧붙이기, 점화식을 레지스터 꼴로, 차례 없는 모으기; knapsack 10.8, matmul 36.4, join 118 ms, roots 8.87로 계층 9.57보다 빠름). BFS를 셈하니 차례대로 할 꼬리가 간선의 약 10%라, 꼬리를 묶음의 C로 하고 간선을 레인으로 펼치면 계층과 겨룰 만하다(약 6–7 ms 예상). 가드, 묶음 안의 걷기(함께, 펼치기), 걸러내기를 만들어 BFS가 묶음으로 돈다([130](../research/130-batch-executor-frontiers.md): 93 → 41 ms, 계층 7.7). 간선당 명령은 211개(계층 38.5)이고, 후보만 골라 돌게 해도 연산마다 레인을 훑는 꼴의 바닥이 약 50개라, 계층을 넘으려면 합친 연산의 표가 필요하다. 사용자는 범용의 길을 택했다(2026-10-08). 같은 코드의 값 다시 쓰기, `if !c`의 분기 하나, 고르기(적은 레인의 갈래를 그 레인만), 비트로 묻기, 아핀 `first + i`로 BFS가 간선당 112개, 24.0 ms가 됐다([131](../research/131-batch-executor-general-cuts.md)); 범용의 바닥은 계층의 약 2배로 보인다. 레지스터 타일(값을 NEON 레지스터에 두는 해석기)을 시제품으로 재니 산수의 사슬(roots)은 C보다 빠르지만 모으기가 있는 몸체(배낭 4배, BFS 2.4배)에서 졌고, 같은 자리에서 꽉 짠 훑기는 BFS 1.2배·배낭 0.4배였다([133](../research/133-register-tiles-measured.md)): 꼴은 옳고, 묶음 프로그램을 로드 때 한 번 만들고 루프를 타입·폭·보폭으로 꽉 짜는 것이 길이다. 묶음마다의 값을 다시 재니 배낭에서 0.90 µs, 시간의 38%이고(132의 "6,500 명령"은 세 배로 잘못 나눈 수였다), 블록의 모양에서 나오는 사실은 로드 때 한 번, 값에서 나오는 사실만 묶음마다 보는 묶음 프로그램을 다섯 단계로 설계했다([025](025-batch-programs.md)). 그 1–4단계를 만들었다(134–137: knapsack 11.46 → 8.25 ms로 기계어의 1.04배, matmul 40.0 → 26.3, roots 8.97 → 8.28; 남은 큰 틈은 matmul의 안쪽 회차마다의 해석과 BFS의 후보 꼬리).
+작성일: 2026-10-05. 상태: 제안과 시제품([041](../../research/experiments/041-batches/)). 1단계를 만들었다([126](../research/126-batch-executor-stage-one.md): knapsack이 실행기에서 1,041 ms → 13.6 ms, 안쪽 루프는 계층의 1.17배로 기준 미달). 2단계(중첩 루프를 함께, 모으기)를 만들었다([127](../research/127-batch-executor-stage-two.md): roots 9.13 ms로 계층 9.57보다 빠름, matmul 50 ms). 4단계의 앞 절반(덧붙이기), 5단계(점화식), 칸에 더하기, 걷기를 만들었다([128](../research/128-batch-executor-tables-and-costs.md): join이 실행기의 423에서 161 ms, 계층 74). 남은 값은 덧붙이기 하나 약 100 ns, join의 점화식 회차당 약 60 ns, 레인이 적은 중첩 몸체의 해석이고, BFS는 아직 묶이지 않는다(바뀌는 개수, 커지기만 하는 걸러내기, 중첩 걷기가 필요하다). 그 셋을 줄였다([129](../research/129-batch-executor-costs-cut.md): 한꺼번에 덧붙이기, 점화식을 레지스터 꼴로, 차례 없는 모으기; knapsack 10.8, matmul 36.4, join 118 ms, roots 8.87로 계층 9.57보다 빠름). BFS를 셈하니 차례대로 할 꼬리가 간선의 약 10%라, 꼬리를 묶음의 C로 하고 간선을 레인으로 펼치면 계층과 겨룰 만하다(약 6–7 ms 예상). 가드, 묶음 안의 걷기(함께, 펼치기), 걸러내기를 만들어 BFS가 묶음으로 돈다([130](../research/130-batch-executor-frontiers.md): 93 → 41 ms, 계층 7.7). 간선당 명령은 211개(계층 38.5)이고, 후보만 골라 돌게 해도 연산마다 레인을 훑는 꼴의 바닥이 약 50개라, 계층을 넘으려면 합친 연산의 표가 필요하다. 사용자는 범용의 길을 택했다(2026-10-08). 같은 코드의 값 다시 쓰기, `if !c`의 분기 하나, 고르기(적은 레인의 갈래를 그 레인만), 비트로 묻기, 아핀 `first + i`로 BFS가 간선당 112개, 24.0 ms가 됐다([131](../research/131-batch-executor-general-cuts.md)); 범용의 바닥은 계층의 약 2배로 보인다. 레지스터 타일(값을 NEON 레지스터에 두는 해석기)을 시제품으로 재니 산수의 사슬(roots)은 C보다 빠르지만 모으기가 있는 몸체(배낭 4배, BFS 2.4배)에서 졌고, 같은 자리에서 꽉 짠 훑기는 BFS 1.2배·배낭 0.4배였다([133](../research/133-register-tiles-measured.md)): 꼴은 옳고, 묶음 프로그램을 로드 때 한 번 만들고 루프를 타입·폭·보폭으로 꽉 짜는 것이 길이다. 묶음마다의 값을 다시 재니 배낭에서 0.90 µs, 시간의 38%이고(132의 "6,500 명령"은 세 배로 잘못 나눈 수였다), 블록의 모양에서 나오는 사실은 로드 때 한 번, 값에서 나오는 사실만 묶음마다 보는 묶음 프로그램을 다섯 단계로 설계했다([025](025-batch-programs.md)). 다섯 단계를 모두 만들었다(134–138: knapsack 11.45 → 8.19 ms로 기계어의 1.04배, matmul 40.0 → 25.8, roots 8.97 → 8.29, join 117.9 → 110.5, BFS 24.2 → 23.2; 남은 큰 틈은 matmul의 안쪽 회차마다의 해석과 BFS로, 이 해석기 꼴의 바닥이다).
[023](023-optimizing-tier.md)의 계층은 루프 묶음을 SSA로 올려 AArch64 기계어를 실행 시간에 만든다. 일곱 가지 일의 기하 평균을
C의 3–300배에서 1.92배로 줄였지만([125](../research/125-tier-first-steps.md)), 실행 가능한 메모리에 코드를 쓰고 리눅스 AArch64에서만
diff --git a/docs/design/025-batch-programs.md b/docs/design/025-batch-programs.md
index 994e52e..b5190b0 100644
--- a/docs/design/025-batch-programs.md
+++ b/docs/design/025-batch-programs.md
@@ -15,7 +15,10 @@ matmul ≤ 21 ms). 4바이트 곱하기 뒤 matmul은 26.57 ms다(명령 −20%)
일이고, 이 설계는 합친 연산을 두지 않는다(133과 같은 결론). 4단계를 닫았다([137](../research/137-batch-executor-stage-four.md): 열을 제자리에서
읽고(보기, `pass`를 넘어 남는 여섯 자리는 복사) 비교가 레인을 세어 knapsack 10.07 → 8.25 ms(막대 ≤ 8.6, 기계어의 1.04배). 남은 차는
커널이 아니라 묶음마다의 해석(약 3 ms)이다. 남은 큰 틈은 matmul의 안쪽 회차마다의 해석(루프마다 미리 짠 프로그램의 일)과 BFS의 후보
-꼬리(5단계)이고, 어느 쪽으로 갈지는 사용자의 몫이다). [024](024-batch-executor.md)가 처음에 적고 만들지 않은 단계("묶음 프로그램은 루프마다 한 번
+꼬리(5단계)이고, 어느 쪽으로 갈지는 사용자의 몫이다). 5단계를 닫아 이 설계를 마쳤다([138](../research/138-batch-executor-stage-five.md): 칸에 더하기를 정수로 한 번에(join 116.0 →
+110.5, 막대 ≤ 108 못 미침: 남은 것은 같은 칸의 메모리 이어짐), `decide`의 비트를 한 번(BFS 23.2, 막대 ≤ 21.5 못 미침: 꼬리의 시간은
+명령보다 메모리), 묶음 실행기의 루프 정렬(커밋마다 6% 흔들리던 놓인 자리를 멈춤). b9 대비 matmul −36%, knapsack −28%, roots −7.6%,
+join −6.2%, BFS −3.9%; 실행기, 은행 질의, TPC-C는 그대로). [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).
@@ -177,9 +180,9 @@ matmul은 j를 레인 200개로 묶고 k를 레인이 함께 돈다. 묶음은 2
|---|---:|---:|---:|---:|---:|---:|---:|---:|
| knapsack | 11.5 ms | ≤ 10.3 (10.34) | ≤ 9.2 (10.51) | 3단계 10.07, 4단계 ≤ 8.6 | | 4.55 (꽉 짠 두 훑기 1.87) | 7.9 | 6.25 |
| matmul | 40.0 | ≤ 39 (38.2) | ≤ 33 (36.1) | ≤ 21 (곱하기 뒤 26.57) | | 17.75 | 14.2 | 7.6 |
-| BFS | 24.0 | ≤ 24 | ≤ 22 | ≤ 21 | ≤ 21.5 (b16에서 다시) | 10.3 (꽉 짠 9.8) | 7.7 | 9.9 |
+| BFS | 24.0 | ≤ 24 | ≤ 22 | ≤ 21 | ≤ 21.5 (b16에서 다시; 23.21) | 10.3 (꽉 짠 9.8) | 7.7 | 9.9 |
| roots | 8.97 | ≤ 9.0 | ≤ 8.6 | | | 6.8 | 9.57 | 6.86 |
-| join | 117 | ≤ 116 | ≤ 115 | | ≤ 108 (b16에서 다시) | 57.8 | 84.6 | 31.7 |
+| join | 117 | ≤ 116 | ≤ 115 | | ≤ 108 (b16에서 다시; 110.5) | 57.8 | 84.6 | 31.7 |
| 묶음마다 (배낭) | 0.90 µs | ≤ 0.60 (0.68) | ≤ 0.45 | | | | | |
| 묶음마다 (sum0, `icount`) | 2,330 명령 | ≤ 1,550 (1,822) | ≤ 1,300 | | | | | |
diff --git a/docs/research/138-batch-executor-stage-five.md b/docs/research/138-batch-executor-stage-five.md
new file mode 100644
index 0000000..d3e46bf
--- /dev/null
+++ b/docs/research/138-batch-executor-stage-five.md
@@ -0,0 +1,79 @@
+# 138 — 묶음 실행기 5단계: 꼬리 둘, 그리고 루프의 정렬
+
+2026-10-09. [025](../design/025-batch-programs.md)의 마지막 단계다. 걸러내기 후보의 꼬리(BFS)와 칸에 더하기의 꼬리(join)를 줄였고, 뜨거운 루프가
+놓이는 자리가 커밋마다 흔들리던 것을 빌드 플래그 하나로 멈췄다. 코드는 `956a6cf`(칸에 더하기를 한 번에), `ad44b68`(`decide`가 비트를 한 번),
+`2ce9ce1`(루프 정렬)과 `3e76a60`(정렬은 묶음 실행기에만)이다. 같은 방법으로 쟀다(`b17`, `b18`, `b18a`, `b19`, `b20`; 번갈아, 명령 수).
+
+## 쉬운 말로
+
+- **막대 셋에 못 미쳤다.**
+ - join은 116.0에서 110.8 ms다(막대 ≤ 108). 칸에 더하기의 꼬리는 약 17에서 약 12 ms로 줄었다. 회차의 루프는 이제 정수뿐이고 명령이
+ 합 하나에 약 20개인데, 회차당 약 30 사이클이 든다. 남은 것은 같은 칸을 읽고 더해 쓰는 메모리의 이어짐이다(16행에 행이 되풀이된다).
+ 그것을 끊는 범용의 길(묶음 안에서 행마다 먼저 모으고 칸에는 한 번 쓰기)은 행 수만큼의 자리가 있어야 하고, 이 설계 밖이다.
+ - BFS는 23.98에서 23.17 ms다(막대 ≤ 21.5). `decide`는 2.5에서 1.6 ms가 됐다. 132의 "후보당 250"은 명령이었는데, 꼬리의 시간은
+ 대부분 그것이 읽는 메모리(비트, 남긴 회차)였다. 그래서 명령을 반 가까이 덜어도 시간은 3분의 1만 돌아왔다. 남은 꼬리 약 5.2 ms에
+ 1.5 ms를 넘는 것이 하나도 없어 멈췄다.
+ - knapsack은 4단계에서 8.25 ms였는데, 이 단계의 첫 커밋 뒤에 같은 명령으로 8.74 ms가 됐다(사이클 +3.4%). 놓인 자리다. 루프 정렬 뒤
+ 8.21 ms다.
+- **루프 정렬**(`-falign-loops=64`, `BATCH_OPTIMIZE`). 묶음 실행기의 루프가 각각 64바이트 줄의 처음에서 시작한다. 이것이 없으면 파일의
+ 어디를 고쳐도 뜨거운 루프가 움직였다(`words_op`의 더하기 루프가 64바이트 줄의 28, 60, 20 …에서 시작). 정렬하자 묶음으로 도는 일의
+ 사이클이 모두 줄었다(knapsack 5.3%, matmul 1.9%, BFS 1.5%, join 1.2%). 명령은 채움 때문에 0.7–2.3% 늘었다. 처음에는 리스트의 루프도
+ 정렬했는데(`LIST_OPTIMIZE`를 함께 써서), 은행 질의의 명령이 b9보다 0.8–2.0% 늘고(기준 1%) 사이클은 오르내려 이득이 없었다. 그래서
+ 묶음 실행기에만 준다.
+- **b9(025를 시작하기 전)와 견주면**(마지막 빌드 b20): matmul 40.0 → 25.8 ms(−36%), knapsack 11.45 → 8.19(−28%), roots 8.97 → 8.29(−7.6%),
+ join 117.9 → 110.5(−6.2%), BFS 24.15 → 23.21(−3.9%), sort 57.0 → 55.9, words 215.3 → 217.7(묶이지 않는 일).
+
+## 만든 것
+
+- **칸에 더하기를 한 번에**(`sums_in_words`). 회차 0부터, 먼저 행과 e를 4바이트로 바꾸고(벡터 코드) e가 2^32 아래의 정수가 아닌 첫 회차를
+ 찾는다. 그 앞의 회차는 회차마다 합을 차례대로 한다: 1·4바이트 칸은 정수로, 제 바이트에 드는 동안. 8바이트 칸은 수로, 유한한 동안.
+ 실행기의 더하기가 만들 수와 같고, 결함도 넓히기도 없다. 처음으로 아닌 회차와 합에서 지금의 길(회차마다, 넓히기와 결함을 그 회차에서)이
+ 이어서 돈다. 그 앞에서 아무것도 넓어지거나 결함이 나지 않았으므로 되돌릴 것이 없다.
+ - 고정 소스 `SUMS_SOURCE`를 더했다: 16행이 되풀이되는 표, 묶음 중간에 1바이트에서 4바이트로 넓어지는 열, 2^32를 넘어 8바이트로 가는 열,
+ 가린 회차에 더하는 분수. 메시지 여섯에서 7,912 회차를 한 번에, 3,228 회차를 지금의 길로 돌고, 세 길(묶음, 실행기, 기계어)이 같다.
+ - 처음 짠 것은 8바이트 칸을 받지 않아 열 하나가 8바이트면 묶음 전체가 지금의 길로 갔다. 이 소스가 그것을 보였다(13 묶음 모두 회차 0에서).
+- **`decide`의 비트를 한 번**. 남긴 회차마다 키를 비트 자리로 한 번 바꾸고, 걸러내기가 더하는 키가 물은 키와 같으면 그 자리를 쓴다(걸러내기는
+ 그렇다). 우리 비트를 지울 때는 세운 비트의 낱말만 지운다(세울 때 적어 둔다). BFS 명령 −0.4%.
+- **루프 정렬**. 위와 같다(묶음 실행기에만).
+
+## 잰 것
+
+| 일 | b9 | b16 (4단계 끝) | b17 (칸) | b18 (`decide`) | b19 (둘 다 정렬) | b20 (묶음만 정렬) | 기계어 | C |
+|---|---:|---:|---:|---:|---:|---:|---:|---:|
+| matmul | 39.99 ms | 26.27 | 26.36 | 26.24 | 25.76 | **25.77** | 14.2 | 7.6 |
+| knapsack | 11.45 | 8.26 | 8.74 | 8.74 | 8.20 | **8.19** | 7.9 | 6.25 |
+| roots | 8.97 | 8.28 | 8.29 | 8.28 | 8.30 | **8.29** | 9.57 | 6.86 |
+| join | 117.9 | 116.0 | 111.8 | 111.4 | 110.9 | **110.5** | 84.6 | 31.7 |
+| BFS | 24.15 | 23.98 | 24.24 | 23.56 | 23.11 | **23.21** | 7.7 | 9.9 |
+
+- 명령(b9 → b20, 묶음 길): matmul −23.7%, knapsack −11.7%, roots −7.3%, join −4.0%, BFS 0.0%(`decide`의 −0.5%와 정렬의 채움).
+- 실행기만의 길: 다섯 일 모두 b9와 같은 명령이다(±0.1%). 리스트의 루프까지 정렬했던 b19에서는 BFS가 +1.0%였다.
+- 원본: `041-batches/2026-10-08-pairs.jsonl`과 `2026-10-09-pairs.jsonl`, `2026-10-08-counts.jsonl`과 `2026-10-09-counts.jsonl`(측정이
+ 날짜를 넘었다); 은행 질의 `040-against-c/2026-10-09-counts-s19.jsonl`(둘 다 정렬), `2026-10-09-counts-s20.jsonl`(묶음만).
+
+## 뜻과 맞음
+
+- 커밋마다 시드 1, 2, 3, 5, 23의 무작위 프로그램(기본과 `PURE_SELECT=all`), 고정 소스의 세 길(두 모드; GRAPH의 `exists`, `memory`, `work`
+ 결함이 제 회차에), 단위 시험이 지났다.
+- 단계의 끝에서(`2ce9ce1`) 단위 시험 세 모드, ASan 단위 시험(기본과 실행기 모드), sanitized `pure-run`의 고정 소스(두 모드, `SUMS_SOURCE`와
+ GRAPH 포함)와 시드 23의 프로그램(두 모드), aup의 검사 두 모드가 지났다. Pure의 `make check`는 다시 `server_view_workers`의 시간 초과로
+ 한 번 멈췄고(4단계 끝과 같은 흔들림), 홀로 세 번 지나고 다시 돈 전체가 지났다. 정렬을 묶음 실행기에만 준 `3e76a60`에서 단위 시험 네
+ 모드와 고정 소스(두 모드)를 다시 돌렸다.
+- 은행 질의의 명령 수(`3e76a60`)는 b9와 ±0.9% 안이다(같은 빌드에서도 1% 안팎 오가는 수). TPC-C(번갈아 여섯 번)는 b9 121.5만 대 지금
+ 122.4만 신규 주문/분(+0.7%)이다.
+
+## 025를 닫으며
+
+다섯 단계의 막대와 잰 수:
+
+| 단계 | 막대 | 잰 수 |
+|---|---|---|
+| 1. 블록의 사실을 로드 때 | knapsack ≤ 10.3 ms, 묶음마다 ≤ 0.60 µs | 10.34, 0.68 (못 미침) |
+| 2. 균일·아핀 조각 | knapsack ≤ 9.2, ≤ 0.45 µs, matmul ≤ 33 | 10.51, 0.63, 36.1 (못 미침) |
+| 3. 꼴과 폭에 맞춘 커널 | matmul ≤ 21, sum0 ≤ 1.5 | 26.6 (못 미침), 1.01 |
+| 4. 훑기의 수 | knapsack ≤ 8.6 | 8.25 |
+| 5. 꼬리 | BFS ≤ 21.5, join ≤ 108 | 23.17, 110.8 (못 미침) |
+
+못 미친 막대는 모두 셈이 빠뜨린 것이 있었다(1단계: 명령마다의 머리, 2단계: 조각이 덮는 명령의 값, 3단계: 회차마다의 해석과 행렬 만들기,
+5단계: 명령과 메모리의 시간). 남은 큰 틈은 둘이다. matmul의 안쪽 회차마다의 해석(약 14 ms)과 BFS(기계어의 3배)의 노드와 간선마다의 해석이다.
+둘 다 이 해석기 꼴의 바닥(135)이고, 루프마다 미리 짠 프로그램이 그 수단이다. 025 밖이다.