aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-19 02:28:34 +0100
committereunhhu <[email protected]>2026-10-19 02:28:34 +0100
commitca214af2684b3c6e6982d1fe8be4e7ecc0849b00 (patch)
tree9710ca0c2e1671ea03ae17980632bcd5fbf03f6f /docs
parentf12346827a136536c5e8e9c6af3da842bd5cc0c3 (diff)
downloadpure-ca214af2684b3c6e6982d1fe8be4e7ecc0849b00.tar.gz
pure-ca214af2684b3c6e6982d1fe8be4e7ecc0849b00.zip
140's 3, inflate (RFC 1951), written in aup with no new opcode (aup's examples/inflate.aup, codes read a bit at a time from canonical counts as zlib's puff.c): 640 streams zlib made (6,488,480 bytes: random bytes, words, runs and repeats 32,768 back; levels 0, 1, 6, 9 and zlib's five strategies, so stored, fixed and dynamic blocks) inflated to the original's bytes on the three paths, the paths' outputs the same bytes; 240 damaged streams (cut, a bit flipped) read as inflate.py (the Python reference) reads them, 165 refused by both and 75 the same bytes; module_specs in make check: 144 streams on the three paths, 21 cut ones failing and leaving the last bytes; bars: written (no new opcode), three workarounds (lists passed in records: a fn takes no list; the output a list of one: a bytes value is no state; a block's tables built in a fn: a let's list lives in the handler's own block), 132 lines against Python's 81, 1,152 ns an output byte against the same algorithm in C 10.7 (108x) and zlib 3.5 (329x), pure Python 1,214; 191 instructions a byte (a block fn called for each bit was 246 and 1,553 ns: small block fns' inlining is 2c's leftover); the four timing tools' message calls fixed for 4b's API (no keys); CHECKPOINT's direction row
Diffstat (limited to 'docs')
-rw-r--r--docs/research/140-spec-suite.md63
1 files changed, 61 insertions, 2 deletions
diff --git a/docs/research/140-spec-suite.md b/docs/research/140-spec-suite.md
index e0e1b13..d6f6a26 100644
--- a/docs/research/140-spec-suite.md
+++ b/docs/research/140-spec-suite.md
@@ -28,7 +28,9 @@
- **직접 확인할 것.** `research/experiments/044-specs/run.sh`가 탐침을 컴파일하고 진단을 낸다.
- **다음 결정.** 계산의 핵을 설계한다. 여섯 가지와 고정 한도, 상수 시간 암호가 핵이 받아야 할 최소의 후보다.
- **027 뒤(2026-10-19).** 핵을 다섯 단계로 다시 세웠다. 탐침의 필요는 셋을 빼고 모두 컴파일된다: 보기에서 재귀 fn을
- 부르기, 레코드나 갈래 하나인 상태, 커서 자리의 글자(아래 "027 뒤의 탐침"). 묶음에서 쓴 것은 2(SHA-256)와 4(JSON)다.
+ 부르기, 레코드나 갈래 하나인 상태, 커서 자리의 글자(아래 "027 뒤의 탐침"). 묶음에서 쓴 것은 2(SHA-256), 4(JSON), 3(inflate)이다.
+ inflate는 새 명령 없이 써졌고 zlib의 스트림을 모두 맞게 푼다. 다만 우회가 셋(리스트를 받는 fn, 바이트 하나인 상태, 블록 안의
+ 리스트)이고, 줄은 파이썬의 1.6배, 시간은 같은 알고리즘의 C의 108배다.
## 묶음
@@ -42,7 +44,7 @@
|---|---|---|---|---|---|---|
| 1 | DNS 메시지(RFC 1035) | 와이어 프로토콜 | 바이트 읽고 쓰기, 빅엔디언, 비트 필드, 이름 압축(바이트 안의 참조) | 알려진 구현이 만든 질의·응답 바이트와 대조 | 못 쓴다(바이트, 비트) | 처음 필요한 것(바이트, 비트)이 컴파일된다. 쓰지 않음 |
| 2 | SHA-256(FIPS 180-4) | 해시 | 32비트 낱말, 회전, xor | NIST CAVP의 SHA 벡터 | 우회로만 될 것으로 보인다(써 보지 않음) | 썼다: NIST 벡터를 세 실행 길에서 지난다(1단계) |
-| 3 | inflate(RFC 1951) | 압축 | 비트 읽기, 허프만 표, 뒤 참조 | zlib로 압축한 말뭉치를 풀어 원본과 비교 | 못 쓴다(바이트, 비트) | 처음 필요한 것(바이트, 비트)이 컴파일된다. 쓰지 않음 |
+| 3 | inflate(RFC 1951) | 압축 | 비트 읽기, 허프만 표, 뒤 참조 | zlib로 압축한 말뭉치를 풀어 원본과 비교 | 못 쓴다(바이트, 비트) | 썼다: zlib 스트림 640개, 손상 240개가 파이썬 참조와 같다. 우회 셋, 줄 수 못 미침, C의 108배(아래) |
| 4 | JSON(RFC 8259) → 트리와 질의 | 깊이를 모르는 데이터 | 글자 읽기, 중첩, 재귀, 갈래 | JSONTestSuite | 못 쓴다(글자, 중첩, 재귀, 갈래) | 썼다: JSONTestSuite와 무작위 문서가 파이썬과 같다(2a–2c) |
| 5 | UTF-8과 줄 나누기(UAX #14) | 텍스트 | 바이트, 코드 포인트, 속성 표 | Unicode의 `LineBreakTest.txt` | 못 쓴다(글자) | 글자 읽기가 컴파일된다(2c). 쓰지 않음 |
| 6 | 관계 질의, 그래프 탐색 | 데이터 모델 | 리스트, 키, 접기, 이벤트의 리스트 | TPC-C, 은행 질의, 파이썬 모델 | 된다(기준선: [092](092-tpcc.md), 040의 은행 질의, aup `routes.aup`) | 된다. TPC-C가 SQLite와 오차 없이 같다(5단계) |
@@ -458,6 +460,63 @@
- 커서 자리의 글자를 읽지 못한다.
- 셋 다 핵의 변경이다. 묶음의 명세를 더 쓰며 둘 이상이 필요로 하는지 본 뒤에 정한다(쌓임을 막는 규칙).
+## 묶음의 3: inflate (2026-10-19)
+
+027 뒤에 처음 쓴 명세다. 새 명령도 코어의 C도 없이 aup와 Pure만으로 썼다(aup의 `examples/inflate.aup`). 원시 deflate
+스트림(RFC 1951)을 그 바이트로 푼다. 부호는 표준 부호의 길이별 수에서 한 비트씩 읽는다(zlib의 `puff.c`와 같은 길). 그래서 걸음마다
+명세의 것이다. deflate가 아닌 스트림은 그 이벤트를 실패시킨다.
+
+**맞음.**
+- `inflate_check.py`: zlib가 만든 스트림 640개(6,488,480바이트)가 세 실행 길(묶음, 실행기만, 기계어)에서 모두 원본과 같고, 세
+ 길의 출력이 바이트까지 같다.
+ - 내용은 0–70,000바이트의 무작위 바이트, 낱말, 짧은 반복, 32,768바이트 뒤의 반복이다.
+ - 수준 0·1·6·9와 zlib의 다섯 전략(기본, filtered, 허프만만, RLE, 고정)이라 저장·고정·동적 블록을 모두 지난다.
+- 손상된 스트림 240개(자른 것과 비트 하나를 뒤집은 것)를 같은 길로 읽는 파이썬 참조(`inflate.py`)와 견준다. 165개는 둘 다
+ 거절하고, 75개는 같은 바이트를 낸다.
+- Pure의 `make check`(`module_specs`)에 들어갔다. 스트림 144개(1.2 MB)를 세 길에서 풀고, 반으로 자른 21개는 이벤트가 실패해 앞
+ 스트림의 바이트를 남긴다.
+
+**막대.**
+
+| 막대 | 잰 것 | |
+|---|---|---|
+| 쓸 수 있다 | aup와 Pure만. 새 명령 없음 | 지남 |
+| 우회가 없다 | 셋(아래): 레코드에 담아 넘긴 리스트, 항목 하나의 리스트에 둔 바이트, fn으로 옮긴 블록의 표 | **못 미침** |
+| 줄 수 ≤ 파이썬 참조 | aup 132줄(닫는 괄호만인 줄 18) 대 파이썬 81줄 | **못 미침** |
+| C 대비 시간 | 낱말 65,536바이트(22,991바이트로 압축)에서 출력 바이트당 1,152 ns. 같은 알고리즘의 C(10.7 ns)의 **108배**, zlib(표로 읽는다, 3.5 ns)의 329배. 순수 파이썬은 1,214 ns(Pure가 1.05배 빠름) | 기록(막대 없음) |
+| 견고하다 | 위의 맞음, 세 길이 같음, 예산 안에서 끝남. 세 기계는 아직 돌리지 않았다 | 일부 |
+
+**우회 셋(언어의 틈).**
+1. fn이 리스트를 인자로 받지 않는다(`'counts' is a list`). 갈래나 레코드의 값은 리스트를 담은 채 넘어간다(json_tree의 `walk`).
+ 그래서 부호의 두 표를 레코드 `Code`에, 길이들을 레코드 `Lengths`에 담아 넘긴다. `Lengths`는 우회만을 위한 레코드다.
+2. 바이트나 레코드 하나를 상태로 두지 못한다(027 뒤의 탐침의 둘째 틈). 출력을 항목 하나의 리스트 `inflated`에 두고, 이벤트마다
+ 지우고 덧붙인다. 묶음의 명세가 이 틈에 처음 닿았다.
+3. let의 리스트는 핸들러의 맨 바깥 블록에만 두고, 비우지 못한다(`[]`를 대입하지 못한다). 블록마다 새로 짓는 길이의 표를 fn
+ `codes`로 옮겼다(fn은 부를 때마다 제 리스트를 가진다). 줄은 거의 늘지 않았지만, 구조가 명세가 아니라 언어를 따른다.
+
+**줄을 길게 한 것.** 132 대 81이다. 닫는 괄호의 줄을 빼면 114다.
+- 닫는 괄호의 줄이 18줄이다(파이썬은 들여쓰기).
+- 우회의 레코드 셋(`Code`, `Lengths`, `Inflated`)과 항목 하나를 비우는 줄이 11줄이다.
+- 비트 자리를 옮기는 줄(`at = next`, `i = i + 2` 같은 것)이 11줄이다. 파이썬은 `Bits` 객체(8줄)가 자리를 들고 간다. aup의 fn은
+ 값만 돌려주므로 부른 쪽이 자리를 따로 더한다.
+- 리스트의 선언이 5줄이다(파이썬은 쓰는 자리에서 짓는다).
+
+**시간이 가는 곳.** 출력 바이트당 Pure 명령이 191개이고, 명령 하나에 약 6.0 ns가 든다.
+- 처음 쓴 꼴은 decode가 비트마다 블록 fn `take(d, at, 1)`를 불렀다. 그때는 246개, 1,553 ns였다. 비트 하나를 식 하나의 fn `bit`로
+ 쓰니 인라인되어 명령이 23%, 시간이 26% 줄었다. 블록 fn의 부름 하나가 그만큼 무겁다. 작은 블록 fn의 인라인은 2c의 남은 것이다
+ (컴파일러의 일).
+- 기계어 계층(`PURE_JIT=all`)은 빠르게 하지 못한다(1,151 대 1,152 ns). 바이트 읽기, fn 부름, 리스트 덧붙이기를 실행기에 맡겨서다.
+ SHA-256과 같은 까닭이다.
+- 같은 날 SHA-256은 바이트당 1,479 ns(C의 163배)다. 1단계의 1,591 ns보다 조금 빠르다.
+
+**원본.** `research/experiments/044-specs/`
+- `inflate.py`: 파이썬 참조
+- `inflate_check.py`: 맞음과 줄 수
+- `inflate_time.c`: 같은 바이트로 같은 알고리즘의 C, zlib, Pure의 시간. 셋의 출력이 원본과 같아야 잰다.
+- `2026-10-19-inflate.jsonl`: 잰 수
+- 그 사이 Pure의 API가 바뀌어(4b, 메시지의 키가 빠졌다) 컴파일되지 않던 시간 도구 넷(`time.c`, `json_time.c`, `json_tree_time.c`,
+ `json_rec_time.c`)의 메시지 부름을 고쳤다.
+
## 착수 기준 (다음 일)
- **바꿀 결정.** 계산의 핵을 설계한다(027). 위 표의 여덟 후보 가운데 무엇을 어떤 모양으로 받을지 정한다.