aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-09 15:57:59 +0100
committereunhhu <[email protected]>2026-10-09 15:57:59 +0100
commit6761c28b4f4d580083bdd49b23d6626dd973cfb8 (patch)
tree3fa274efb2ab50e1a113fac4e793d5b8ef22a643 /docs
parent75aa418924fbaf510174713aa007482169bbd114 (diff)
downloadpure-6761c28b4f4d580083bdd49b23d6626dd973cfb8.tar.gz
pure-6761c28b4f4d580083bdd49b23d6626dd973cfb8.zip
Research 139: what a program made at load per batch loop would take (the deciding prototype 043): the executor's extra over 041 is interpretation, not semantics (matmul 11.1 ms of it, checks 0.2); the program interpreted a step at a time, with the executor's checks, ran matmul's loop in 10.36 ms and knapsack's in 4.16 (straight kernels 7.17 and 3.76; the executor about 22.2 and 7.3), about 14 and 5.4 ms with the making (bars 15 and 6.5; the compiled code 14.2 and 7.9); the buffer rule it found (registers at load, two buffers each so a result never lands on its operand, 24 lanes apart); the suite's five bodies sorted (matmul, knapsack, join's order loop straight-line; roots and BFS not); 026's scope as straight-line programs with the pass for the rest, and its risk
Diffstat (limited to 'docs')
-rw-r--r--docs/research/139-batch-programs-at-load.md51
1 files changed, 49 insertions, 2 deletions
diff --git a/docs/research/139-batch-programs-at-load.md b/docs/research/139-batch-programs-at-load.md
index ba4a0c9..dbfaef1 100644
--- a/docs/research/139-batch-programs-at-load.md
+++ b/docs/research/139-batch-programs-at-load.md
@@ -3,6 +3,18 @@
2026-10-09. 025를 마친 뒤 남은 큰 틈(matmul의 안쪽 회차, knapsack의 묶음마다)이 무엇인지 재고, 루프마다 로드 때 짠 프로그램(026이 될 것)이
그것을 얼마나 줄일 수 있는지 시제품으로 정한다. 사용자의 말: "헷갈리는 것이 있으면 연구부터" (2026-10-09).
+## 결과 (2026-10-09)
+
+- **막대를 넘었다(시제품).** 로드가 짤 프로그램을 손으로 옮겨 한 걸음씩 해석하면 matmul의 본 루프가 10.36 ms(같은 커널을 바로 부르면
+ 7.17), knapsack이 4.16 ms(바로 3.76)다. 만들기를 더하면 matmul 약 14 ms(막대 ≤ 15, 기계어 14.2), knapsack 약 5.4 ms(막대 ≤ 6.5,
+ 기계어 7.9, C 6.25)다. 지금의 실행기는 25.8과 8.19 ms다. matmul의 안쪽 회차 하나에 해석이 약 82 ns(지금 약 280 ns)이고, knapsack의
+ 묶음 하나에 약 0.08 µs(지금 약 0.6 µs)다.
+- **짤 수 있는 범위.** 스위트의 몸체 가운데 matmul, knapsack, join의 주문 루프는 곧은 줄(스칼라 걸음과 레인 걸음, 균일한 횟수의 중첩
+ 루프, 가린 쓰기, 끝의 칸에 더하기)이라 그대로 짜인다. roots(레인마다 다른 회차에 나가는 중첩 루프)와 BFS(레인 공간이 바뀌는 펼친 걷기,
+ 걸러내기)는 곧은 줄 밖이다.
+- **그래서 026은 "곧은 줄의 프로그램, 나머지는 지금의 `pass`"다.** 2단계의 조각을 레인 걸음, 균일한 중첩 루프, 미룬 쓰기로 넓힌 꼴이다.
+ join은 거의 움직이지 않는다(그 110 ms는 점화식 38, 해시 찾기 19, 칸에 더하기 12로 모두 커널이다).
+
## 쉬운 말로 (실험 전에 적은 것)
- **남은 값은 뜻의 검사가 아니라 해석이다.** b20에서 matmul(25.8 ms)의 값은 커널 8.7, 해석 11.1(명령을 읽고 `Val`을 쌓고 꼴을 묻기),
@@ -45,8 +57,43 @@ matmul의 해석 11.1 ms(안쪽 회차 4만 번, 회차마다 약 660 사이클)
## 결정할 실험
-(아래에 채운다)
+`research/experiments/043-programs/programs.c`. 몸체를 바이트코드 그대로의 걸음으로 옮겼다. 스칼라 걸음(필드, 지역 변수, 루프 변수,
+곱·더하기·빼기를 실행기의 규칙으로: 수는 유한, 아핀은 정수 검사; DENSE 키의 칸 읽기)과 커널 걸음(4바이트 레인: 열 읽기(4바이트면 보기,
+1바이트면 넓혀 복사), 곱하기, 더하기, 비교와 1의 수)을 하나의 `switch`로 한 걸음씩 돈다. 레인 레지스터는 로드 때 정한다. 리스트의 칸은
+묶음에 한 번, 행의 범위는 양 끝, 2^32 너머는 묶음의 실패, 미룬 쓰기는 끝에서 거리를 본 뒤 가린 채로 쓴다. 작업량도 센다. 같은 커널을
+C에서 바로 부르는 길(해석 없음)과 natural.h의 C와 견줬다. 답은 모두 같았다.
+
+| 몸체 | 프로그램 | 바로 | 실행기 (b20) | 기계어 | C |
+|---|---:|---:|---:|---:|---:|
+| matmul (만들기 빼고) | **10.36 ms** | 7.17 | 약 22.2 | | 4.9 (n 상수) |
+| knapsack (만들기 빼고) | **4.16** | 3.76 | 약 7.3 | | 6.23 |
+
+- 명령(3번): matmul 프로그램 2.58억 대 바로 1.72억(안쪽 회차마다 약 715 명령, 걸음 12개에 걸음당 약 60), knapsack 1.51억 대 1.41억.
+- 걸음당 약 60 명령의 대부분은 스칼라 걸음이 아핀 값마다 정수 검사를 세 번씩 다시 하는 값이다. 로드가 범위를 아는 값(루프 변수의
+ 양 끝, 필드가 정수인지, `i*n`이 2^52 안인지)은 로드 때 증명할 수 있으니, 바닥은 이보다 낮다. 막대는 그것 없이 넘었다.
+
+**두 번 돌아간 길이 설계의 내용이다.** 처음에는 matmul의 프로그램이 20 ms였다. 하나는 `s = s + e`를 제자리에서 해서(결과가 피연산자와
+같은 버퍼) 컴파일러의 겹침 검사가 벡터 루프를 버렸고, 하나는 레인 레지스터가 정확히 4 KB씩 떨어져 있었다. 실행기가 이미 하는 대로(결과를
+피연산자에 쓰지 않음, 버퍼를 2의 거듭제곱 간격에 두지 않음) 바꾸자 10.4 ms가 됐다. 그래서 026의 버퍼 배정 규칙은 이렇다: 레인 레지스터를
+로드 때 정하고, 레지스터마다 버퍼 둘을 두어 결과가 피연산자에 떨어지지 않게 번갈아 쓰고, 버퍼는 24 레인씩 어긋나게 둔다. 025가 미뤄 둔
+"버퍼 자리를 로드 때"(`sweep` 2.0 ms)가 이 규칙으로 풀린다.
## 짤 수 있는가: 스위트의 몸체 다섯
-(아래에 채운다)
+| 몸체 | 걸음 | 곧은 줄로 짜이는가 |
+|---|---|---|
+| matmul 안쪽 (j 레인, k 함께) | `i*n+k`, `k*n+j`(스칼라), DENSE 칸 읽기, 1바이트 열 읽기, 곱하기, 더하기; k는 균일한 횟수; 끝에 `total += s`(레인 모으기), `last = s` | 예 |
+| knapsack (항목마다) | `c = cap - k`(아핀), 열 보기 둘, 더하기, 비교와 1의 수, 가린 미룬 쓰기와 거리 검사 | 예 |
+| join 주문 루프 | 점화식 `x`(회차마다, 지금의 동작 목록), 레인 산수(나누기, 내림), 레인 행의 칸 읽기, HASHED 키 찾기(레인마다), 칸에 더하기(끝에) | 예 (점화식과 해시 찾기는 지금의 커널 그대로) |
+| roots (i 레인, 뉴턴 걸음) | 레인마다 다른 회차에 나가는 중첩 루프(`break`), 가린 지역 변수 쓰기, 가린 모으기, 실수 나누기 | 아니오: 레인마다의 나감 마스크가 있어야 한다 |
+| BFS 프런티어 | 가드, 행마다의 조각 걷기를 간선 레인으로 펼치기(레인 공간이 바뀜), 걸러내기(`has`, 덧붙이기, 끝의 결정) | 아니오: 레인 공간 둘과 걸러내기 |
+
+**026의 범위(제안이 아니라 연구가 보인 꼴).** 곧은 줄의 블록(갈래가 없거나 가린 쓰기로 끝나는 갈래, 균일한 횟수의 중첩 루프, 끝의 모으기와
+칸에 더하기)을 로드 때 걸음으로 짜고, 묶음을 열 때 꼴(폭, 경계)이 맞으면 그 걸음을 돌고, 아니면 지금의 `pass`가 돈다. 2단계 조각의 자리를
+레인 걸음까지 넓힌 것이다. 셈: matmul 25.8 → 약 14 ms, knapsack 8.2 → 약 5.5 ms. join, roots, BFS는 이 범위에서 움직이지 않는다. roots와
+BFS는 그 다음 단계(레인마다의 나감 마스크; 레인 공간 둘과 걸러내기)의 일이고, 수는 아직 없다.
+
+**위험.** 곧은 줄의 걸음에 대해서는 지금 `pass`가 하는 뜻을 다시 짜게 된다: 묶음마다 폭으로 고르는 커널, 넘치면 4바이트에서 수로 다시,
+미룬 쓰기와 의존 검사, 읽기의 기록, 작업량, 결함의 회차. 한 뜻에 두 구현이 서는 것은 025가 `pass`를 고쳐 나가며 피한 위험이다. 2단계에서
+통한 방법(견줌 빌드에서 프로그램을 마른 채로 돌리고 `pass`도 돌려 견주고, 단계가 끝나면 견줌을 지움)을 그대로 쓰되, 이번에는 스칼라가
+아니라 레인 배열을 비트까지 견줘야 하므로 더 무겁다.