aboutsummaryrefslogtreecommitdiffstats
path: root/docs/research/140-spec-suite.md
blob: 6231c33a0b4d0ee4b8ffe37b800c9a718e1064af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# 140 — 명세 묶음: 세상의 명세가 aup에서 처음 부딪치는 곳

2026-10-13. [026](../design/026-app-contract.md)은 1.0에서 얼릴 앱 계약을 적었다. 이 연구는 다시 세울 쪽인 계산의 언어를 잴 도구를
세운다. 분야마다 명세 하나를 골라 묶음을 만들고, 오늘의 aup가 각 명세의 첫 줄에서 무엇에 막히는지를 작은 프로그램으로 확인했다.

사용자의 말: "세상의 그 어떤 명세와 개념이든 pure 생태계(vega, aup포함)에서 구현을 가장 유연하고 견고하게 해내야해. 추상화와 input
-> output으로 가는 과정은 얇고 직관적이고 빠르게." (2026-10-13)

## 쉬운 말로

- **알아낸 것.** 지금의 aup로는 묶음의 명세 대부분을 첫 줄에서 쓸 수 없다. 탐침 열 개로 확인했다. 막는 것은 여섯 가지다.
  - 바이트의 i번째를 읽지 못한다.
  - 비트 연산(xor, and, shift)이 없다.
  - 레코드 안에 레코드나 리스트를 둘 수 없다.
  - 재귀가 컴파일 오류다.
  - 갈래 타입(이것 아니면 저것)이 없다.
  - text의 글자를 하나씩 읽지 못한다.
- **우회는 있지만 얇지 않다.** 모두 "얇고 직관적"에 어긋나고, 명세의 개념이 언어의 개념에 바로 놓이지 않는다.
  - 바이트는 메시지의 행(수의 리스트)으로 받는다.
  - 비트 연산은 `floor`와 `%`의 반복으로 한다.
  - 트리는 부모 키를 가진 평평한 리스트로 둔다.
  - 재귀는 이벤트의 리스트로 짠 스택으로 한다.
  - 한 번의 타이머는 주기 틱과 `now`로 흉내 낸다.
- **되는 것.** 관계 질의(TPC-C, 은행 질의), 그래프(BFS), 텍스트 편집과 커서, 주기 타이머다. 지금의 패턴이 덮는 곳이다.
- **아직 모르는 것.** 핵을 다시 세운 뒤 명세마다의 줄 수와 시간이다. 수치 막대는 첫 구현을 잰 뒤에 정한다. 막대가 설 수 있는지
  셈부터 하고, 막대를 먼저 세우지 않는다.
- **근거.** aup 컴파일러의 실제 진단(아래 표)과 aup·Pure 설계 문서의 연산 목록이다. 시간은 재지 않았다.
- **직접 확인할 것.** `research/experiments/044-specs/run.sh`가 탐침을 컴파일하고 진단을 낸다.
- **다음 결정.** 계산의 핵을 설계한다. 여섯 가지와 고정 한도, 상수 시간 암호가 핵이 받아야 할 최소의 후보다.

## 묶음

명세를 구현하는 것과 Pure가 그것을 내부에 받아들이는 것은 다르다.
- DNS, JSON, HTTP는 경계의 명세이고, 하이브리드로 남는다.
- SHA-256과 Unicode는 수학이거나 세상의 자료다.
- 배치는 Vega의 일이다.
- 묶음이 재는 것은 "세상의 명세를 Pure 생태계에서 얇게 쓸 수 있는가"이다.

| # | 명세 | 분야 | 처음 필요한 것 | Pure 안의 검증 | 오늘 |
|---|---|---|---|---|---|
| 1 | DNS 메시지(RFC 1035) | 와이어 프로토콜 | 바이트 읽고 쓰기, 빅엔디언, 비트 필드, 이름 압축(바이트 안의 참조) | 알려진 구현이 만든 질의·응답 바이트와 대조 | 못 쓴다(바이트, 비트) |
| 2 | SHA-256(FIPS 180-4) | 해시 | 32비트 낱말, 회전, xor | NIST CAVP의 SHA 벡터 | 우회로만 될 것으로 보인다(써 보지 않음) |
| 3 | inflate(RFC 1951) | 압축 | 비트 읽기, 허프만 표, 뒤 참조 | zlib로 압축한 말뭉치를 풀어 원본과 비교 | 못 쓴다(바이트, 비트) |
| 4 | JSON(RFC 8259) → 트리와 질의 | 깊이를 모르는 데이터 | 글자 읽기, 중첩, 재귀, 갈래 | JSONTestSuite | 못 쓴다(글자, 중첩, 재귀, 갈래) |
| 5 | UTF-8과 줄 나누기(UAX #14) | 텍스트 | 바이트, 코드 포인트, 속성 표 | Unicode의 `LineBreakTest.txt` | 못 쓴다(글자) |
| 6 | 관계 질의, 그래프 탐색 | 데이터 모델 | 리스트, 키, 접기, 이벤트의 리스트 | TPC-C, 은행 질의, 파이썬 모델 | 된다(기준선: [092](092-tpcc.md), 040의 은행 질의, aup `routes.aup`) |
| 7 | 텍스트 편집 | UI 개념 | text, 커서 | 편집기의 참조 모델 대조(aup `make check`) | 된다 |
| 8 | 상자 배치(flex 같은) | UI 개념 | 중첩, 재귀 | 기존 배치 구현(Yoga, Taffy)의 결과와 대조 | 못 쓴다(중첩, 재귀) |
| 9 | HTTP/1.1 요청을 패킷마다(RFC 9112) | 스트림의 상태 기계 | 바이트, 이벤트를 넘는 상태 | 경계를 아무 데서나 자른 요청 모음 | 못 쓴다(바이트). 지금은 `serve/`의 C다 |
| 10 | 재시도, 물러서기, 시간 제한 | 시간 | 시각, 한 번의 타이머 | 시각을 준 시뮬레이션 | 우회(주기 틱 `every`와 `now`) |
| 11 | X25519, ChaCha20-Poly1305 | 상수 시간 암호 | 데이터에 따라 달라지지 않는 시간 | RFC 7748, RFC 8439의 벡터 | aup 밖(설계상, 아래) |

**11은 핵의 원시 연산으로 둔다(가설).** 실행기의 시간은 데이터에 따라 달라진다. 묶음 실행기도 결함이 나면 묶음을 처음부터 실행기로
다시 돈다. 그래서 상수 시간을 언어로 약속하기 어렵다. 지금도 암호는 `link/`의 C와 OpenSSL에 있다.

## 기준 (명세마다)

1. **쓸 수 있다.** aup와 Pure만으로 쓴다. 코어에 C를 더하지 않는다.
2. **우회가 없다.** 명세의 개념이 언어의 개념에 바로 놓인다. 바이트는 바이트이고 트리는 트리다.
3. **얇다.** 줄 수를 깔끔한 참조 구현(파이썬)과 견준다.
4. **빠르다.** 시간을 C의 참조 구현과 견준다. 막대는 첫 구현을 잰 뒤 정한다.
5. **견고하다.** 위 표의 검증을 모두 지나고, 예산 안에서 끝나고, 세 기계(Pi, WSL2, Mac)에서 같은 답을 낸다. 검증은 Pure의
   검사(`make check`) 안에서 돈다. 이것이 Pure 생태계 안의 표준이다.

**쌓임을 막는 규칙.** 핵에는 묶음의 명세 둘 이상이 필요로 하는 것만 넣는다. 하나만 필요로 하는 것은 라이브러리다. 지금의 명령
90개가 판마다 패턴 하나씩 쌓인 길(026)을 다시 걷지 않기 위해서다.

## 탐침

명세가 처음 필요로 하는 것을 가장 작은 aup 프로그램으로 써서 컴파일했다(aup `build/aup`, 2026-10-13). 원본은
`research/experiments/044-specs/probes/`, 출력은 `2026-10-13-probes.tsv`다.

| 탐침 | 쓴 것 | aup의 진단 |
|---|---|---|
| `p1-byte` | `packet.data[0]`(bytes 필드의 첫 바이트) | `expected newline or semicolon` |
| `p2-bits` | `(h ^ word.w) << 3` | `unexpected character '^'` |
| `p2b-bits` | i64의 `h & word.w` | `unexpected character '&'` |
| `p3-nested` | 레코드 안의 `children: list of Node` | `expected a numeric field default` |
| `p3b-nested` | 레코드 안의 `at: Point` | `a field is a number …, a bool …, text … or an i64` |
| `p4-rec` | 자신을 부르는 `fn depth(n)` | `fn 'depth' calls itself` |
| `p5-chars` | `for c in line.t` | `unknown list 'line.t'` |
| `p5b-chars` | `line.t[0]` | `expected newline or semicolon` |
| `p5c-cursor` | 커서 자리의 글자 `note.char(at)` | `unknown library 'note'` |
| `p6-variant` | `state x: Leaf \| text` | `unexpected character '\|'` |

진단이 아니라 연산 목록에서 확인한 것도 있다.
- Pure의 명령에 바이트를 자리로 읽거나 쓰는 것이 없다. 바이트 값에는 `LEN`과 text 변환(`TEXT_FROM`, `BYTES_FROM`)뿐이다(008).
- text에는 길이(`.chars`, `.lines`, `.bytes`), 커서가 있는 줄(`TEXT_LINE`), 넣기와 지우기, 비교, 잇기가 있고, 글자 하나를 읽는 것은
  없다(aup 002).
- 고정 한도가 형식에 있다: 상태 32, 입력 32, 레코드 16, 레코드의 필드 16, 키별 컬렉션 1, 모듈의 코드 65,535 명령(`core/module.h`).
  fn은 부르는 곳마다 인라인되므로, 큰 명세의 구현은 코드 한도에 먼저 닿을 수 있다.

## 핵이 받아야 할 최소 (가설)

| 후보 | 그것을 필요로 하는 명세 |
|---|---|
| 바이트를 자리로 읽고 쓰고 자르기 | 1, 2, 3, 5, 9 |
| 정수의 비트 연산(and, or, xor, shift, rotate) | 1, 2, 3 |
| 중첩(레코드 안의 레코드와 리스트) | 4, 8 |
| 갈래(합 타입) | 4, 8, 9(파서의 상태) |
| 재귀(깊이를 실행량과 메모리 예산에 센다) | 4, 8 |
| text를 글자의 열로 읽기 | 4, 5 |
| 뜻에서 구현의 숫자 빼기(고정 한도) | 3, 4, 5(표의 크기) |
| 상수 시간 암호의 원시 연산 | 11과 링크 |

**재귀는 끝남과 결정성을 깨지 않는다.** 이벤트가 끝나는 것을 실제로 보증하는 것은 문법(상한 있는 반복, 재귀 금지)이 아니라
예산이다. `for i in 0 .. 1e15`가 어느 기기에서나 같은 자리에서 `work`로 멈추는 것도 예산 덕이다. 재귀도 그 깊이를 실행량과
메모리에 세면 같은 보증 아래 둘 수 있다. 끝이 없는 일(스트림, 긴 계산)은 이벤트마다 상한 있는 걸음 하나를 하고, 이어갈 자리를
상태에 둔다(9가 그 꼴이다). 이 가설이 맞다면 aup의 제한 일부는 불변의 핵이 아니라, 패턴을 모으던 때의 안전장치다. 핵 설계에서
확인한다.

## 의심

- **text와 바이트를 따로 둘 것인가?** text는 "UTF-8로 검사한 바이트"의 한 견해일 수 있다. 4와 5가 같은 열을 다른 단위(바이트,
  글자)로 읽는다.
- **리스트, 키 있는 리스트, 인덱스, 커서를 하나로 모을 수 있는가?** 026의 3번 의심과 같다. 중첩이 들어오면 리스트는 값이 되고, 키와
  앵커는 그 위의 견해가 될 수 있다.
- **메시지의 행은 중첩의 땜질인가?** 레코드의 리스트를 메시지에만 허락한 것은 중첩이 없어서 낸 길로 보인다. 중첩이 들어오면 따로 둘
  까닭이 없다.

## 027의 1단계: SHA-256 (2026-10-13)

[027](../design/027-computation-core.md)의 1단계를 만들었다.
- **Pure.** 명령 넷이다.
  - `I64_BITS`: and, or, xor, 두 시프트. `~x`는 xor −1이다.
  - `BYTE_AT`: 자리의 바이트.
  - `BYTES_OF`: i64 리스트의 바이트.
  - `INDEX_AT`: 자리의 항목. 끝을 넘으면 멈춰 서지 않고 `not_found`다.
- **aup.**
  - 16진 리터럴, 파이썬의 우선순위를 따르는 비트 연산자, `b[i]`
  - `list of i64`(`number`, `bool`, `text`)와 그 리터럴, 자리로 읽고 쓰기, `bytes(xs)`
  - 한 번에 주기 `a, b = x, y`와 `let a, b = …`
- **SHA-256.** aup의 `examples/sha256.aup`에 썼다.

**맞음.** 공식 벡터를 Pure 안에서 돌려 맞는지 본다.
- 대조한 메시지는 142개다: FIPS 180-2의 예 넷("", "abc", 448비트, 896비트), 채움의 모든 경계를 덮는 길이 0–129, 255–32,000바이트.
- 다이제스트가 세 실행 길(묶음, 실행기만, 기계어)과 기본 길에서 모두 `hashlib`와 같다.
- 이 대조는 Pure의 `make check`(`module_assumptions`, 메시지 135개)에 들어갔다.
- aup의 검사에는 다음이 들어갔다.
  - 진단 12건
  - 받아들이는 꼴 하나
  - 무작위 비트 식 192개: 사건 768개, 그중 넘침과 한도 실패 196개가 파이썬의 정수와 같다
  - 바이트와 리스트의 앱 하나
- Pure 단위 시험(`bits_and_bytes`)은 비트 연산의 끝값 27개, 바이트 읽기 11개, 거절 7개를 본다.

**막대.**

| 막대 | 잰 것 | |
|---|---|---|
| 공식 벡터 모두 | 142/142, 세 길 | 지남 |
| 줄 수 ≤ 파이썬 참조 | aup 52줄(닫는 괄호만인 줄 7) 대 파이썬 28줄 | **못 미침** |
| C 대비 시간 | 65,536바이트에서 바이트당 1,591 ns 대 C 9.0 ns: **176배**. 순수 파이썬은 3,708 ns(Pure가 2.3배 빠름) | 기록(막대 없음) |
| 실행기만의 길, 다섯 일의 명령 ±0.1% | matmul +0.43%, BFS −0.34%, 나머지 셋 ±0.03% | **못 미침**(아래) |
| 은행 질의의 명령 ±1% | 질의 일곱 개가 −0.22%에서 0.00% | 지남 |

원본: `research/experiments/044-specs/`
- `check.py`: 맞음과 줄 수
- `time.c`: 같은 바이트로 C와 견준 시간. 다이제스트가 같아야 잰다.
- `sha256.py`: 파이썬 참조
- `2026-10-13-sha256.jsonl`: 잰 수

**기존 일의 명령 수.**
- 비교 대상은 b21(338c665)과 b22(41a2e5c)다. 워크로드 모듈은 두 빌드에서 바이트까지 같다(aup의 새 문법은 옛 소스의 코드를 바꾸지
  않는다). 그래서 차이는 실행기의 코드에서만 온다.
- 실행기만의 길(PURE_BATCH=0):
  - knapsack, roots, join은 +0.02%, 0.00%, +0.02%다.
  - matmul은 +0.43%, BFS는 −0.34%다. 부호가 엇갈리므로 일이 아니라, `step`에 경우 넷을 더해 컴파일러가 코드를 다르게 짠 것이다.
  - 묶음의 길도 −0.36%에서 +0.14%다.
- 그 넷을 인라인되지 않는 함수 하나로 빼 보았다(b23). 실행기만의 길에서 명령이 6–15% 늘었다(roots +15.4%, matmul +11.1%,
  BFS +6.3%). `step` 안에서 `frame`을 넘기는 호출 하나가 실행기 전체의 값을 메모리로 밀어내는 절벽이다. 그래서 되돌리고 인라인의
  꼴을 둔다. 막대(±0.1%)에는 못 미친 채로 적는다.
- 은행 질의(s21 대 s22, 각 두 번)는 −0.22%에서 0.00%다. 막대(±1%) 안이다.
- 원본: `041-batches/2026-10-13-counts.jsonl`(b21, b22, b23), `040-against-c/2026-10-13-counts-s22.jsonl`.

**줄을 길게 한 것.** 처음 쓴 것은 72줄이었다. 한 번에 주기가 없어서 상태 여덟 개를 옮기는 데만 24줄이 들었다. 그것을 1단계에
넣어(싸고 범용이다) 52줄이 됐다. 남은 차이는 다음이다.
- 형식 지정이 없다. 16진 다이제스트에 7줄이 들었고, 파이썬은 `f'{x:08x}'` 한 줄이다.
- 바이트를 잇거나 자르지 못한다. 채움에 fn 하나와 바이트마다의 조건이 들었고, 낱말 만들기에 5줄이 들었다. 파이썬은 `data + b'\x80' + …`와
  `int.from_bytes` 한 줄씩이다.
- 리스트를 값으로 짓지 못한다. 메시지 스케줄을 블록마다 다시 만들지 못하고, 64칸을 미리 채워 덮어쓴다.
- `let`의 리스트는 핸들러 맨 바깥에만 둔다. 그래서 해시값을 리스트 대신 이름 여덟 개에 두었다.
- 닫는 괄호의 줄이 7줄이다.

앞의 넷은 값의 일이라 2단계에 넣는다. 027의 2단계에 형식 지정(16진, 자릿수), 바이트 잇기와 자르기, 값으로서의 리스트를 더한다.

**시간이 가는 곳.** 블록 하나에 Pure 명령 19,129개가 든다.
- 비트 연산이 21%다.
- fn 호출의 몫(인자를 꺼내는 PICK 16%, 리터럴 CONST 16%, 시프트 수를 바꾸는 I64_FROM 7%)이 39%다.
- `rotr(x, 7)`의 7은 상수인데도 fn을 펼칠 때 접히지 않아, `32 - n`을 매번 수로 계산하고 i64로 바꾼다. 인라인에서 리터럴 인자를 접으면
  명령의 3분의 1 남짓이 줄 것으로 본다. 컴파일러의 구현 일이다.
- 명령 하나에 약 5.3 ns가 든다. 값의 칸(`Slot`)이 바이트 참조를 함께 들어 PICK마다 48바이트를 옮기는 꼴이다.
- 기계어 계층은 이 루프를 빠르게 하지 못했다. `PURE_JIT=all`이어도 시간이 같다. 새 명령마다 실행기의 걸음을 불러서다.
- 바이트와 비트의 일을 빠르게 하는 것은 핵이 선 뒤의 일이다(027). 그 과녁은 이제 수치 루프가 아니라 이런 일이다.

## 027의 2a: JSON 검사기 (2026-10-13)

2a에서 더한 것:
- **Pure.**
  - 바이트 자르기, 한 바이트
  - 이름으로 담는 바이트와 text 32칸. 칸 번호가 타입을 정하고, 덧붙이기는 제자리에서 하며, 담은 양은 몫에 센다.
  - NUM_TEXT의 형식, CONCAT의 바이트
  - `pure-run`이 메시지의 바이트를 파일에서 읽는 길(`message NAME @PATH`)
- **aup.**
  - 형식 `{x:08x}`, `{p:.2f}`, 잇기 `+`, 자르기 `b[a .. c]`, `byte(n)`
  - 바이트와 text의 let(`s = s + e`는 덧붙이기), `while`
  - `&&`와 `||`의 단락 평가

**JSON 검사기.** aup의 `examples/json.aup`(80줄)는 RFC 8259의 문법을 처음부터 한 번 읽는다. 열린 배열과 객체는 스택(i64 리스트)에
둔다.
- **맞음.** JSONTestSuite(1ef36fa)에서 틀린 것이 없다.
  - `y_` 95개를 모두 받고 `n_` 188개를 모두 거절한다.
  - `i_` 35개 가운데 21개를 받는다: 범위를 넘는 수, 짝 없는 서로게이트 이스케이프, 500겹.
  - 14개는 거절한다: UTF-8이 아닌 입력, BOM, UTF-16.
  - 세 실행 길의 출력이 바이트까지 같다.
  - 이 대조는 Pure의 `make check`(`module_specs`)에 들어갔다. 케이스는 `tests/specs/json`에 넣었다(MIT). 큰 둘은 검사가 만들고 해시로
    대조한다.
- **줄 수.** 같은 설계의 파이썬 참조 `json_check.py`가 90줄이고 aup가 80줄이다. 막대를 지난다.
- **시간.** 10만 바이트 문서에서 바이트당 299 ns다. C로 쓴 같은 설계는 2.57 ns(116배), 파이썬은 246 ns(Pure가 1.2배 느림)다. 바이트당
  Pure 명령이 339개다.

**쓰다가 드러난 뜻의 틈.**
- 처음 돌리자 받을 것이 모두 `limit`으로 실패했다. aup의 `&&`가 양쪽을 다 계산해서, `i < n && space(d[i])`가 끝에서 `d[n]`을 읽었다.
- 범위를 먼저 보고 읽는 꼴은 명세 구현의 기본이라 `&&`와 `||`를 단락 평가로 바꾸었다. 식은 아무것도 바꾸지 않으므로, 달라지는
  것은 오른쪽이 실패할 수 있을 때뿐이다.
- 그래서 컴파일러는 오른쪽이 실패할 수 없을 때 지금처럼 AND나 OR 하나를 낸다. 실패할 수 없는 것은 비교, 상태와 필드 읽기, 걷는
  항목의 필드, 작은 상수를 더하는 수의 덧셈, 그리고 왼쪽이 이미 계산한 식이다.
- TPC-C의 `order_status` 필터는 오른쪽의 키 찾기와 곱셈을 왼쪽이 이미 계산한다. 그래서 모듈이 바이트까지 같고, Pure의 키 범위
  찾기도 그대로다.
- Pure, aup, Vega의 소스 가운데 빌드되는 36개를 전후로 견주었다. 달라진 것은 JSON 검사기 하나다.

**기존 일의 명령 수(2a).**
- b24(e0276bf)의 워크로드 모듈은 b22와 바이트까지 같다.
- 실행기만의 길에서 1단계(b22) 대비 knapsack이 +0.14%, 나머지 넷은 ±0.06% 안이다. b21 대비로는 1단계의 matmul +0.43%, BFS
  −0.31%가 그대로 남는다.
- 은행 질의(s22 대 s25, 각 네 번)는 이렇다.
  - 흔들림이 작은 넷(`large`, `none`, `field`, `found`)은 +0.28%에서 +0.80%로 꾸준히 늘었다.
  - 흔들림이 큰 셋(`per_branch`, `in_credit`, `ranks`)은 평균 +0.17%에서 +0.45%다. 같은 빌드의 두 번 사이에서도 0.3–1% 오간다.
  - 모두 막대(±1%) 안이다.
  - 이벤트마다 칸 32개를 비우는 고리를 그 칸을 쓰는 모듈에만 두었지만(e38cb1c) 수는 그대로였다. 늘어난 몫은 `step`에 경우 넷이
    더해진 데서 온다. 1단계와 같은 꼴이다.
- 원본: `041-batches/2026-10-13-counts.jsonl`(b24), `040-against-c/2026-10-13-counts-s24.jsonl`, `-s25.jsonl`, `-s25b.jsonl`.

**SHA-256 다시.**
- 형식(`"{h0:08x}…"`)과 바이트 잇기(채움)로 다시 써 42줄(닫는 괄호만인 줄 5)이 됐다. 파이썬은 28줄이다.
- 남은 차이는 리스트를 값으로 짓지 못해 생기는 것(메시지 스케줄의 미리 채움), 낱말 만들기, 선언이다.
- 시간은 바이트당 1,437 ns(C의 159배)다. 블록당 명령이 16,476개로 줄었다.

## 착수 기준 (다음 일)

- **바꿀 결정.** 계산의 핵을 설계한다(027). 위 표의 여덟 후보 가운데 무엇을 어떤 모양으로 받을지 정한다.
- **남은 질문.**
  - 핵을 일반화해도 은행 질의와 TPC-C가 그대로인가?
  - 바이트와 비트 일의 시간이 C와 얼마나 떨어지는가? 지금 그런 일은 실행기로 돈다.
- **최소 확인.** 핵의 첫 꼴로 2(SHA-256)와 4(JSON)를 먼저 쓴다. 둘이 탐침 여섯을 모두 덮는다: 2는 바이트와 비트, 4는 글자, 중첩,
  재귀, 갈래다. 공식 벡터를 지나는지, 줄 수, C 대비 시간을 잰다.
- **중단 기준.** 한 명세만 필요로 하는 원시 연산이 핵에 들어가야 하면 멈추고 라이브러리 길을 찾는다. 일반화한 핵에서 은행 질의의
  명령 수가 1%, TPC-C가 3%를 넘게 나빠지면 026의 중단 기준을 따른다.