행렬 선언 — 1급 엔티티 검토
상태: 도입되었습니다. 이 문서는 결정의 근거이고, 표기와 구현 상태는 매트릭스 선언에 있습니다.
결정을 가른 것은 2절의 인코딩이 아니라 5.1절의 표기였습니다. 6절이 재어 둔 대로 비용은 코드 생성에 몰렸고, 형식 · 런타임 · 익스포터 · 히스토리는 한 파일도 닿지 않았습니다.
table · enum · const · struct 옆에 matrix를 다섯 번째 선언 종류로 두는 것을
검토합니다. 기대하는 것은 둘입니다 — M×N이라는 구조에서 오는 인코딩 이득과, 그 구조를
아는 생성 코드.
결론 요약
| 확인한 것 | 결과 |
|---|---|
| 인코딩이 줄어드는가 | 거의 아닙니다. 격자는 이미 배열 컬럼 하나로 실리고, 그 스트림은 스칼라 컬럼과 같은 인코딩을 전부 받습니다 |
| 인코딩 이득이 있는 자리는 어디인가 | 축의 선택입니다 — 행 우선으로 펼치는 것이 지금 고정되어 있습니다. 이것은 선언 종류 없이 인코더에서 됩니다 |
| 표기가 나아지는가 | 예. 이것이 가장 큰 이득입니다 — 지금은 「이름이 정수인 컬럼」이라는 판독 규칙이고, 그래서 두 번째 축이 정수 키로 제한됩니다. 선언이 있으면 축의 키는 그 축의 타입입니다 |
| 생성 코드가 나아지는가 | 둘이 나아집니다 — 두 축 조회가 한 번이 되고, 두 테이블이 같이 배포되어야 하는 제약이 없어집니다 |
| 구현 비용 | 코드 생성에 거의 전부 몰립니다. 와이어 kind를 두지 않으면 형식 · 런타임 · 익스포터 · 히스토리가 전부 무변경입니다 — 6절 |
| 권고 | 엔티티는 보류합니다. 9절의 순서로 진행하고, 그 결과가 부족할 때 다시 봅니다 |
1. 지금 격자를 표현하는 방법
셋이 이미 있습니다.
| 방법 | 형태 | 어디에 |
|---|---|---|
| 행 + 위치 배열 | { id, value: T[N] } × M행, 그리고 컬럼 id를 담은 옆 테이블 | 매트릭스 표. 이름 기반 레이아웃에 구현되어 있습니다 |
| 복합 키 | key="attacker,defender" 로 M×N행 | 주 레이아웃 §3.5 |
| 배열의 배열 | 한 행에 T[][] | 다중 중첩 |
매트릭스 표가 이미 답한 것이 이 검토의 출발점입니다 — 그 문서의 결론은 「매트릭스는 새 형태가 아니라 이미 표현할 수 있는 테이블의 다른 표기」 였고, 그래서 코어가 한 줄도 바뀌지 않았습니다. 이 검토는 그 판단을 뒤집을 근거가 생겼는지를 봅니다.
로드맵에 있는 것과 구분합니다. 주 레이아웃의 매트릭스 표는 읽기가 한쪽 레이아웃에만 있다는 문제이고, 선언 종류와 무관합니다. 그쪽이 사용자가 실제로 막히는 자리입니다.
2. 인코딩 — 새 선언 종류가 줄이는 바이트
219 × 482 = 105,558칸 격자를 기준으로 셉니다.
| 파일에 실리는 것 | 지금 | matrix kind가 있으면 | 차이 |
|---|---|---|---|
| 값 105,558개 | 배열 컬럼 하나의 원소 스트림. 스칼라와 동일한 후보 전부(DELTA · RLE · DICT · BITPACK · 접두사 공유) | 같습니다 | 0 |
| 행 길이 219개 | 길이 스트림 → 모든 행이 같은 길이이므로 런 하나 | 없어집니다 | 약 3바이트 |
| 행 id 219개 | 정수 컬럼 하나 | kind가 있어도 필요합니다 | 0 |
| 컬럼 id 482개 | 옆 테이블 한 장 | kind 안으로 들어갈 뿐 없어지지 않습니다 | 테이블 헤더·이름 몇백 바이트 |
값 스트림이 그대로인 것이 핵심입니다. v104가 배열 블록에 스칼라와 같은 인코딩 후보를 주었고, v107이 길이 스트림을 런 하나로 접었습니다. 격자에서 아낄 것이 남아 있다면 그것은 원소 스트림 안인데, 새 선언 종류는 그 스트림을 건드리지 않습니다.
컬럼 id 표가 없어지지 않는다는 점도 중요합니다. id에서 자리를 찾는 대응은 격자의 정의에 들어 있는 정보이므로, 테이블로 두든 kind 안에 두든 파일이 담아야 합니다.
판정: 새 선언 종류의 인코딩 이득은 105,558칸에서 1 KB 미만입니다. 이것이 도입 근거가 되지 않습니다.
3. 인코딩 이득이 실제로 있는 자리 — 축의 선택
원소 스트림 안에 남아 있는 것이 하나 있습니다. 펼치는 순서가 행 우선으로 고정되어 있습니다.
배열 컬럼은 행마다 원소를 이어 붙이므로 스트림은 행0의 N개, 행1의 N개, … 입니다. 그런데
DELTA와 RLE가 일하는 것은 값이 닮은 방향이고, 격자에서 그 방향은 시트마다 다릅니다 —
교역품별 가격표라면 같은 교역품(컬럼)끼리 닮고, 지역별 보정치라면 같은 지역(행)끼리 닮습니다.
닮지 않은 축으로 펼치면 DELTA도 RLE도 런을 만들지 못합니다.
| 후보 | 무엇 | 이득 | 비용 |
|---|---|---|---|
| 전치 후보 | 길이가 모두 같은 배열 컬럼에서 열 우선 펼치기도 측정해 작은 쪽을 고릅니다 | 시트에 따라 큽니다. 축이 어긋나 있으면 지금은 인코딩이 통째로 RAW에 남습니다 | 형식 개정 하나 + 모든 런타임의 읽기 형태 |
| 대칭·삼각 | 두 축이 같은 축일 때 절반만 싣습니다 | 값 스트림 절반 | 검출은 쉽지만, 복호가 행마다 반사 재배치입니다. 커서 방식과 맞지 않습니다 |
| 기본값 + 예외 | 성긴 격자를 기본값과 예외 목록으로 | RLE가 이미 거의 같은 것을 합니다 — 실측 격자는 105,558칸 중 빈 칸이 482개뿐이었습니다 | 얻는 것이 적습니다 |
셋 모두 선언 종류를 요구하지 않습니다. 인코더가 보는 조건은 「길이가 모든 행에서 같은 배열
컬럼」 하나이고, 그것은 TcbColumnEncoder.EncodeArray가
이미 손에 들고 있는 정보입니다.
전치가 읽기에서 성립하는지
성립합니다. 생성된 읽기 코드는 행을 먼저 전부 만들고 컬럼 블록을 하나씩 채웁니다. 커서는 순차적이지만(RLE 런과 DELTA 누산기), 채우는 순서는 생성 코드가 정합니다.
지금 for i in 행: for j in 0..N: rows[i].value[j] = cursor.Next()
전치 for j in 0..N: for i in 행: rows[i].value[j] = cursor.Next()
커서는 여전히 앞에서 뒤로 한 번 지나가고, 바뀌는 것은 대입 대상뿐입니다. 다만 배열을 먼저 전부 할당해야 하므로 길이 스트림을 먼저 다 읽는 두 번 훑기가 되고, 그 형태를 모든 언어 템플릿에 더해야 합니다.