diff options
Diffstat (limited to 'docs/research')
71 files changed, 6081 insertions, 0 deletions
diff --git a/docs/research/000-working-method.md b/docs/research/000-working-method.md new file mode 100644 index 0000000..7648fba --- /dev/null +++ b/docs/research/000-working-method.md @@ -0,0 +1,163 @@ +# Pure의 협업 방식 + +2026-09-22 사용자 요청에 따라 정한 역할 분담이다. + +Codex는 문헌 조사, 선행연구와의 차이 검토, 가설·실험 설계, 비교 구현, +정확성 검사, 측정, 결과 해석과 보고서를 맡는다. 일상적인 연구와 되돌릴 수 +있는 구현 선택은 스스로 진행한다. 추측과 측정 결과를 구분하고, 잘 안 된 +결과도 남긴다. + +사용자는 제품의 목적과 취향, 실제 사용·제작 경험을 제공한다. 이론이나 +저수준 구현을 먼저 이해해야 의견을 낼 수 있도록 요구하지 않는다. Codex는 +사용자의 관찰을 재현 가능한 작업과 기술적 검증 질문으로 바꾼다. + +각 연구 보고서의 앞부분에는 다음을 쉬운 말로 쓴다. + +- 이번에 알아낸 것과 아직 모르는 것. +- 사용 경험에 어떤 차이가 생길 수 있는지. +- 근거가 문헌, 계산, 실제 측정 중 무엇인지. +- 구현체에서 사용자가 직접 확인할 구체적인 동작. +- 다음 결정과 그 이유. + +사용자 피드백은 "하려던 일 / 실제로 일어난 일 / 기대했던 일" 정도로 +충분하다. 원인 분석과 해결책 설계는 Codex가 맡는다. 한 사람의 사용 경험을 +전체 사용자의 선호로 일반화하지 않는다. + +2026-09-22 제작 경험 기준 보완: 이미 만든 데모를 조작한 경험을 개발 경험으로 +기록하지 않는다. 사용자가 빈 작업물에서 대상·상태·입력·반응을 정의하고 바꿀 수 +있어야 한다. 새 동작마다 Codex가 실행기 내부를 수정해야 한다면 표현 도구의 +개발 경험을 검증한 것으로 보지 않는다. 자동화된 작성 흐름 검사도 실제 사용자의 +이해·학습·제작 시간 평가를 대신하지 않는다. + +프로젝트의 가치와 기능 우선순위가 크게 달라지는 선택은 대안과 근거를 +정리해 사용자에게 제시한다. 기술적 결정을 사용자에게 떠넘기는 형태로 +논문이나 전문 용어 중 하나를 선택하도록 요구하지 않는다. + +## 시간을 쓰는 기준 + +2026-09-22 사용자 요청에 따라, 작업량보다 **다음 결정을 바꾸는 근거**를 우선한다. +새 작업을 시작하기 전에 다음 네 가지를 짧게 적는다. 별도 보고서는 필요하지 않다. + +- 바꿀 결정: 결과에 따라 어떤 구현·채택·중단 결정이 달라지는가? +- 남은 질문: 기존 문헌·코드·측정으로 아직 답하지 못한 것은 무엇인가? +- 최소 확인: 그 질문을 구분할 가장 작은 조사·계산·실험은 무엇인가? +- 중단 기준: 어떤 결과면 채택·기각하거나 추가 작업을 멈추는가? + +기존 문서·공개 구현과 비용 계산으로 판단할 수 있으면 재구현하지 않는다. +실험이 필요하면 대표 조건 하나와 적절한 기존 해법부터 비교한다. 잔여 비용이 +확인될 때만 입력 행렬·의존성·비교 대상을 늘린다. 특정 후보가 이기는 조건을 +찾으려고 실험을 계속 추가하지 않는다. 필요한 정확성 검증은 유지하되, 변경이나 +새 우려가 없으면 이미 통과한 무거운 검사를 반복하지 않는다. + +새 문법·컴파일러·ABI·커널 우회·범용 기반 기능은 실제 작업에서 필요성이 +확인될 때 구현한다. 앞으로 쓸 수도 있다는 이유만으로 먼저 만들지 않는다. +기존 해법으로 충분하면 재사용하거나 후보를 닫는다. 차별화가 없다는 결론도 +유효한 결과지만, 같은 결론을 반복 확인하는 것은 새 연구 성과로 세지 않는다. + +[007](007-buffer-retention-results.md)·[008](008-json-fanout-results.md)의 공유·복사 +정책 비교는 현재 증거에서 종료한다. 구체적인 새 작업이나 요구 조건이 생길 때만 +재개한다. 다음 후보 P07/P08도 자동으로 비교 구현에 들어가지 않는다. 기존 증분 +해법으로 해결되지 않는 구체적인 비용과 Pure가 바꿀 수 있는 경로를 먼저 확인한다. +그 차이를 설명할 수 없으면 구현을 시작하지 않는다. + +보고는 결정·근거·남은 한계를 중심으로 짧게 한다. 원자료와 재현 방법은 보존하고, +작업 기준을 충족하지 못한 후보마다 새 데모나 긴 보고서를 만들지 않는다. + +## 자체 연구와 기술 구현의 우선순위 + +2026-09-22 추가 요청: 이미 알려진 방법의 재현을 주된 작업으로 삼지 않는다. +Pure의 요구에서 가설·실행 계약을 만들고 직접 구현하는 데 우선적으로 시간을 쓴다. +선행연구는 같은 발상을 반복하거나 성과를 과장하지 않기 위한 짧은 중복 확인과 +비교 대상으로 사용한다. 기존 기술을 배제하거나 새 이름으로 포장하라는 뜻으로 +해석하지 않는다. 직접 작성한 구현과 학술적인 신규성은 별도로 판단한다. + +첫 작업은 [기록을 버릴 수 있는 실행 코어](../../prototypes/forgetful-core/README.md)다. +값·추적 기록의 손실, 조건부 의존성, 작업 한도 초과를 하나의 작은 실행 계약으로 +구현했다. 이 계약 확인을 마친 뒤 같은 현상을 재현하는 실험을 늘리지 않는다. +제품 채택은 실제 작업의 비용과 기능을 근거로 결정한다. + +이 문서는 현재 작업에서의 역할 분담을 기록한다. 지속적인 백그라운드 실행이나 +정기 보고를 설정한 것은 아니다. + +## 목적과 아키텍처에 대한 사용자 정정 + +2026-09-23: 주 목적은 효율적인 인터페이스이며, 그 아래의 불필요한 추상화를 최대한 제거한다. +기존 앱·런타임·프레임워크의 경계와 개념을 필수 전제로 삼지 않는다. +입력에서 실제 출력까지의 의미와 비용에서 설계를 시작한다. 현재 DSL·VM·캐시도 교체 가능하다. +대용량 데이터·서비스·UI 작업은 전체 애플리케이션 런타임의 실용성과 규모를 검증한다. +첫 검증 작업의 범위가 제품 전체의 정의를 대신하지 않는다. +사용자에게 필요한 표현력·조작·제작 경험과 메모리·계산 비용을 함께 검증한다. +세부 로드맵은 [010](010-roadmap.md)을 따른다. 이전 CLI 우선 추천안은 이 정정으로 대체한다. + +같은 날 핵심 관점 보완: net·RPC·DB·UI는 입출력과 내부 연산을 연결하는 서로 다른 영역이다. +Pure의 중심은 이들 사이에서 무엇을 어디에 연결하고, 어떤 데이터 표현·수명·실행 시점으로 +연결을 효율적으로 수행할지다. 기능 목록이나 공통 API의 수가 연구 성과를 대신하지 않는다. +한 작업의 전체 경로에서 제거한 변환·복사·보관·대기와 그 대가를 확인한다. 이 목표를 현재 +구현으로 이미 해결한 성능 결과와 구분한다. 연결 모델을 특정 그래프·이벤트 버스 구현으로 +먼저 고정하지 않는다. + +참고 대화 후 보완: 개발자가 데이터·연산·소비 관계를 작성하면, 준비 단계와 런타임이 그 +관계에서 불필요한 변환·전달·보관을 제거할 수 있어야 한다. 공통 API나 연결부 수의 증가만으로 +이 목표를 충족했다고 판단하지 않는다. 고정 연결은 준비 단계에서 줄이고 실제 동적 관계에 +필요한 관리 비용을 둔다. 출력과 피드백은 연결 목적지로 표현할 수 있으나 시간 진행·효과의 +재실행 의미를 없애지 않는다. 개발자에게 수명·변환 관리를 떠넘겨 기계 비용만 줄이는 것도 +효율의 완성으로 보지 않는다. 최초의 제한된 작성·준비 구현은 [020](020-prepared-connections.md)이다. + +같은 날 저장 모델 정정: **SQL을 사용하지 않는다. RAM·storage를 SQL이나 관계형 스키마에 +맞추지 않는다.** DB라는 범위 이름은 데이터 접근·갱신·영속성을 다룬다는 뜻이다. 이를 +SQL 엔진 채택으로 해석한 것은 Codex의 오류다. 데이터 배치는 실제 소비 범위·연산·갱신· +수명에서 결정한다. SQL을 자체 질의 언어로 바꾸거나 모든 데이터를 하나의 범용 객체로 +포장하는 것도 자동 해법으로 삼지 않는다. 바이트 접근과 타입·의미 검증의 책임은 구분하며, +SQL 제거만으로 성능 우위를 주장하지 않는다. 018의 SQLite 경로는 역사적 검증 자료로 +보존하고 활성 구현은 [019](019-byte-storage.md)의 직접 바이트 저장으로 대체했다. + +## 독립 네이티브 애플리케이션 런타임과 표준 호환성 + +2026-09-23 추가 정정: **Pure 런타임 자체를 작은 OS로 설계한다.** 실행·메모리·보호·자원· +저장·통신의 운영 모델을 Pure가 소유한다. 현재 OS의 프로세스는 구동 환경이 부과하는 +제약이며 Pure의 기본 개념이나 설계 중심이 아니다. 이를 프로세스 배치·관리 계층으로 +축소하지 않는다. 기존 OS 위에서 개발하더라도 정책은 Pure 코어에, 환경별 수단과 한계는 +플랫폼 연결부에 둔다. UI는 이 작은 OS의 하위 체계다. 현재 프로토타입을 통합 OS 코어로 +구현 완료했다고 보고하지 않는다. [구체적인 경계와 다음 구현](../design/007-execution-domains.md). + +같은 정정의 보완: 외부 통신을 포기하지 않는다. Pure를 사용하지 않는 서비스·런타임· +프로그램과도 양방향으로 통신하고, 외부가 요구하는 프로토콜·형식을 접점에서 지원한다. +Pure끼리의 자체 통신 형식을 모든 상대에게 강제하는 폐쇄 구조로 해석하지 않는다. + +같은 정정의 크기 제약: **Pure는 마이크로 런타임이어야 한다.** 작은 OS라는 표현을 모든 +서비스의 기본 탑재나 코어 비대화의 근거로 삼지 않는다. 필요한 실행·소유권·자원·완료 규칙만 +공통 코어에 두고 나머지는 앱별 선택 구성으로 결합한다. 코드·의존성·유휴 메모리·영역 및 +작업별 관리 비용까지 확인한다. 동적 로더나 범용 서비스 관리자를 선행 구현하지 않는다. + +2026-09-23 사용자 정정: 저장·실행 표현과 미디어 포맷은 새로 설계해도 되지만, +구동 환경과 웹 호환성을 버리지 않는다. 표준 입출력을 유지하는 것과 내부 표현을 +그대로 따르는 것은 별개다. 변환 과정의 복사·동시 보관·품질 비용까지 포함해 판단한다. +현재 SVG 도형 예제나 작은 Wasm 예약 공간을 UI 전체의 메모리 효율로 해석하지 않는다. + +같은 날 추가 정정: **목표는 독립 네이티브 애플리케이션 런타임이며 UI는 그중 일부다. +앱 로직·net·RPC·DB와 영속성을 포함한다.** Codex가 웹 호환성을 브라우저에 실행을 맡기는 +것으로 해석하고, 이어 제품 전체를 UI 런타임으로 한정한 것은 잘못이다. 브라우저 편집기의 +동작 확인은 보존하되 그 실험으로 전체 아키텍처의 범위나 효율을 판단하지 않는다. + +공통 기반은 작업 실행·스케줄링, 데이터·자원의 소유권과 수명, 예산, 비동기 완료·취소·오류와 +과부하 처리의 계약을 다룬다. UI 없는 서버·CLI도 사용할 수 있어야 한다. net/RPC/DB를 +UI 완성 뒤의 부가 기능으로 미루지 않는다. 원격 호출·DB 트랜잭션·UI 조작의 서로 다른 +의미는 보존하며, 공통 API만으로 외부 효과까지 원자적으로 되돌리거나 중복을 없앤다고 약속하지 않는다. + +UI 하위 체계는 배치·텍스트·입력 대상 판정·포커스·조작·렌더링을 책임진다. 플랫폼 연결부는 +파일·소켓·시계·실행 자원뿐 아니라 창·장치 입력·IME·접근성·그래픽·글꼴 서비스를 연결한다. +전문 라이브러리·저장·프로토콜 구현은 위의 내부 모델을 강제하지 않는 범위에서 재사용할 수 있다. 모든 기능을 현재 숫자 VM에 +넣거나 모든 엔진을 직접 작성하는 것은 별도 결정이며 전체 범위를 지원한다는 말과 구분한다. +기능별로 지원하는 계약·검증한 범위·남은 의존성을 명시한다. + +웹은 호환 대상이자 선택 가능한 호스트다. 브라우저 없이 동작하는 경로를 첫 구현 기준에 둔다. +웹의 유연한 작성·연결·갱신 경험을 목표로 삼되, HTML/CSS/DOM의 전면 호환이나 새 브라우저 +엔진 제작을 사용자 요구로 추가하지 않는다. 표준 미디어·문서는 입출력 경계에서 지원하고, +새 내부 표현·포맷의 채택은 필요한 기능과 전체 비용으로 판단한다. + +다음 애플리케이션 작업의 착수 기준: + +- 바꿀 결정: 기존 실행 코어와 연산·수명 초안 중 무엇을 재사용하고, UI·RPC·DB가 공유할 계약을 어디까지 둘 것인가? +- 남은 질문: 비동기 요청과 저장을 연결하면서 중복 데이터·큐·변환을 줄이고 완료·취소·커밋의 의미를 유지할 수 있는가? +- 최소 확인: 기존 작업물의 변경·저장을 로컬 조작과 실제 네트워크 RPC에서 실행하고 DB 결과를 응답·표시에 반영한다. UI 없는 경로도 유지한다. +- 중단 기준: 한 작업의 수명·실패 경계가 확인되면 범용 모듈을 미리 늘리지 않는다. 의미를 잃는 공통화는 수정하고 다음 실제 기능으로 진행한다. 같은 도형·버퍼 실험을 반복하지 않는다. diff --git a/docs/research/001-research-brief.md b/docs/research/001-research-brief.md new file mode 100644 index 0000000..947f10e --- /dev/null +++ b/docs/research/001-research-brief.md @@ -0,0 +1,166 @@ +# Pure: 연구 의제와 첫 검증 설계 + +작성일: 2026-09-22 + +상태: **초기 문헌 조사에 근거한 제안**. 프로젝트의 확정 아키텍처가 아니다. 아래 가설의 신규성과 성능은 아직 검증하지 않았다. 이 초안 작성 시점에는 벤치마크를 실행하지 않았다. + +후속 기록: [첫 합성 실험 결과](002-checkpoint-results.md)와 [협업 방식](000-working-method.md)을 추가했다. 첫 실험은 고정된 계산 사슬에서 보관 간격을 비교하며, 아래의 전체 실행 모델이나 적응형 정책을 구현한 것은 아니다. + +후속 범위 보완: [제작 실험](004-authoring.md)의 사용자 피드백 이후 연산 의미·데이터 수명·모듈 계약을 +먼저 명세하기로 합의했다. 압축·암호화·문서·미디어·OS라는 장기 범위와 현재 구현의 경계는 +[기반 계약 보고서](005-foundation-contract.md)에 기록했다. 아래 초기 가설을 검증 완료로 바꾸는 내용은 아니다. + +## 목표와 연구의 관계 + +2026-09-23 방향 보완: 인터페이스 아래의 불필요한 추상화를 제거하는 것이 중심이다. +아래의 앱 모델·계산 그래프·실행기 구분은 고정 아키텍처가 아니다. +입력부터 출력까지의 표현·전달·메모리 비용을 기준으로 경계 자체를 재설계할 수 있다. +최신 작업 순서는 [010 로드맵](010-roadmap.md)에 기록한다. + +같은 날 범위 정정: Pure는 독립 네이티브 **애플리케이션 런타임**이다. 앱 로직·네트워크·RPC· +DB와 영속성·UI를 다루며, UI는 전체 범위의 일부다. 아래 UI 중심 가설은 한 연구 대상으로 +한정한다. 화면 없는 서버·CLI도 같은 실행 기반을 사용할 수 있어야 한다. + +Pure의 장기 목표는 적은 메모리로 실행되며, 입력과 출력을 유연하게 연결하고, 실행 중에 동작을 관찰·수정할 수 있는 애플리케이션 제작 환경이다. GUI와 터미널에서 같은 작업물을 다루고, 플랫폼이 바뀌어도 작업물의 의미를 보존한다. + +이 목표에는 실행 효율, 표현력, 제작 경험이라는 서로 다른 검증 대상이 있다. 실행기가 가벼워졌다는 결과만으로 제작 경험까지 좋아졌다고 주장하지 않는다. 각각 실험하되 같은 앱 모델과 변경 연산으로 연결한다. + +첫 연구 기여의 후보는 **편집 가능한 상호작용 프로그램에서, 관리 정보까지 포함한 메모리 예산과 응답 지연을 함께 고려하는 정보 보관·재계산 정책**이다. 기존 연구의 적용과 새로운 기여의 경계는 추가 조사와 실험으로 판단한다. + +## ByteDance 사례에서 확인한 방법 + +Sonic의 설계 문서는 당시 서비스 프로파일에서 JSON 직렬화·역직렬화가 상당한 CPU 비용을 차지했다고 보고한다. 여러 기존 구현을 비교한 뒤, 타입별 처리 코드의 특수화, SIMD와 스칼라 처리의 선택, 지연 파싱 등을 병목에 대응시킨다. 이 문서에 담긴 과거 Go 구현의 제약을 현재 모든 환경의 사실로 일반화하면 안 된다. [Sonic 설계 문서](https://github.com/bytedance/sonic/blob/main/docs/INTRODUCTION.md) + +Sonic의 사용 안내는 입력 문자열을 참조하는 방식이 원본 버퍼의 수명을 늘려 상주 데이터량을 증가시킬 수 있다고 설명한다. 따라서 복사량 감소와 메모리 감소는 따로 검증해야 한다. 호환 설정에 따라 정렬·이스케이프 등의 동작도 달라지므로 비교 실험에서는 동일한 의미를 맞춰야 한다. [Sonic README](https://github.com/bytedance/sonic#tips) + +Monoio는 작업이 스레드 사이로 이동하지 않는 실행 모델을 채택하며, 부하가 불균형하면 성능이 떨어질 수 있다는 제한도 명시한다. 특정 실행 조건을 이용해 비용을 줄이고 그 조건이 깨지는 경우까지 보고하는 사례다. [Monoio 설계 목표와 제한](https://github.com/monoio-rs/monoio) + +Pure가 채택할 연구 절차는 다음과 같다. + +1. 실제 작업에서 비용이 큰 위치를 찾는다. +2. 기존 구현과 논문이 이미 해결한 범위, 남는 비용, 성립 조건을 기록한다. +3. 바꿀 표현 또는 알고리즘과 예상 효과를 설명한다. +4. 정확성을 유지한 비교와 구성 요소별 실험으로 효과의 원인을 분리한다. +5. 다른 사람이 재실행할 수 있는 코드·입력·설정·원시 결과를 남긴다. + +## 초기 선행연구 지도 + +| 연구 | 이번에 확인한 내용 | Pure에서 확인할 질문 | +|---|---|---| +| [Self: Dynamic Deoptimization, PLDI 1992](https://bibliography.selflanguage.org/dynamic-deoptimization.html) | 최적화된 실행과 소스 수준 디버깅·실행 중 변경을 연결한다. 공개 논문은 이를 위한 의존성·디버깅 정보의 공간 비용도 다룬다. | 실행과 편집에 필요한 정보 중 무엇을 상주시킬 것인가? 필요할 때 재구성하면 편집 지연은 얼마나 생기는가? | +| [Memory Management for Self-Adjusting Computation, ISMM 2008](https://matthewhammer.org/ismm08/ismm08.pdf) | 변경 대응을 위해 유지하는 실행 기록과 메모리 회수 비용을 직접 연구한다. | 실행 기록이 많은 상황에서 일반적인 메모리 관리가 지불하는 비용은 무엇인가? | +| [Adapton, PLDI 2014](https://www.cs.umd.edu/~mwh/papers/hammer13adapton.html) | 출력의 필요 여부에 따라 재계산하고 기존 계산을 재사용하는 모델이다. | 전체 재계산보다 추적·무효화·보관 비용이 더 커지는 경계는 어디인가? | +| [Dynamic Tensor Rematerialization, ICLR 2021](https://arxiv.org/abs/2006.09616v4) | 제한된 메모리에서 중간 텐서를 버리고 필요할 때 다시 계산하는 온라인 정책을 제시한다. | 계산이 짧고 값이 작은 상호작용 프로그램에서도 정책·메타데이터 비용을 회수할 수 있는가? | +| [Simply RaTT, ICFP 2019](https://arxiv.org/abs/1903.05879) | 신호·이벤트를 표현하면서 의도치 않은 공간·시간 누수를 배제하는 타입 및 실행 규칙을 연구한다. | Pure의 입력·상태 모델에서 이전 상태가 불필요하게 유지되는 것을 어떤 규칙으로 방지할 것인가? | + +확인 범위: 공식 설계 설명, 저자·프로젝트의 초록, 공개 논문의 관련 부분이다. 모든 논문을 정독하거나 실험을 재현한 상태는 아니다. 후속 연구를 망라한 체계적 문헌 조사도 아직 아니다. + +새로운 기여라고 주장할 수 없는 아이디어는 이미 분명하다. 그래프로 계산을 표현하는 것, 변경된 부분만 재계산하는 것, 필요할 때 값을 재구성하는 것, 최적화된 프로그램을 실행 중 수정하는 것 자체에는 선행연구가 있다. Pure의 차별성은 적용 조건, 비용 모델, 표현, 알고리즘, 보장 또는 검증 결과에서 구체화해야 한다. + +## 우선 연구 질문 + +> 실행 중에 연결과 규칙을 수정할 수 있는 상호작용 프로그램에서, 응답 지연을 제한하면서 관리 정보까지 포함한 최대 메모리를 얼마나 줄일 수 있는가? + +일반적인 계산 그래프는 재계산을 줄이기 위해 값, 의존 관계, 변경 여부를 보관한다. 값이 몇 바이트에 불과하고 계산도 짧으면 그 관리 비용이 이득보다 클 수 있다. 이 문장은 Pure가 측정할 가설이다. 아직 Pure에서 확인한 현상이 아니다. + +초기 후보 정책은 작은 계산 묶음마다 다음 두 방식 중 하나를 선택한다. + +- 중간 결과와 변경 정보를 보관해 필요한 부분만 갱신한다. +- 중간 결과와 세부 추적 정보를 줄이고, 해당 묶음이 필요할 때 다시 계산한다. + +앱의 정의와 현재 상태는 보존한다. 제거한 의존성 추적 정보가 있으면, 정의를 순회하거나 더 큰 묶음을 무효화하는 비용이 생긴다. 이 비용까지 포함해 비교한다. + +초기 가설 H1: 변경이 드문 구역과 매번 바뀌는 구역을 구분하는 정책이, 전체 재계산과 세밀한 전체 추적 사이에서 더 유리한 메모리·지연 조합을 제공할 수 있다. + +귀무 가설 H0: 작은 값과 짧은 연산에서는 정책과 관리 정보의 비용 때문에, 잘 구현한 고정 정책보다 이득이 없다. + +단순한 묶음별 고정 정책으로 대부분의 이득이 나온다면 그것을 우선 채택한다. 자동 적응이 필요한지는 별도로 입증한다. + +## 첫 실험이 다룰 의미 + +첫 실험은 단일 스레드의 제한된 실행 모델로 시작한다. 이것은 제품 전체의 표현력이나 동시성에 대한 최종 결정이 아니다. + +- 입력은 순서와 시간이 명시된 기록으로 주어진다. 이벤트, 상태 샘플, 시간 진행을 구분한다. +- 한 처리 단계 안의 순수 계산은 비순환 구조로 제한한다. 피드백은 다음 단계로 넘기는 명시적인 상태를 통한다. +- 외부 입출력과 현재의 변경 가능한 상태를 재계산 가능한 임시 값으로 취급하지 않는다. 재계산으로 출력이 두 번 발생해서는 안 된다. +- 규칙 변경은 처리 단계 사이의 경계에서 적용한다. 초기에는 값·상수·연결·같은 타입의 연산 교체를 지원한다. 타입이나 상태 구조 변경은 명시적으로 거절한다. +- 같은 입력·시간·규칙 변경 기록에 대해 기준 실행기와 같은 상태 및 출력 기록을 생성해야 한다. 부동소수점 비교 규칙과 연산 순서는 사전에 고정한다. +- 처리할 수 있는 모델과 입력 큐의 용량을 명시한다. 예산을 넘으면 명시적인 거절 또는 미리 정의한 역압력으로 처리한다. 조용히 이벤트를 버린 결과를 최적화로 세지 않는다. + +모델 정의와 필수 상태만으로 예산을 넘는 입력은 지원 가능하다고 주장할 수 없다. 추적 정보나 중간 결과를 줄이는 것으로 모든 프로그램을 일정한 메모리에 넣을 수 있는 것은 아니다. + +## 비교 구현과 작업 부하 + +| 구현 | 역할 | +|---|---| +| A: 연속 배열 기반 전체 재계산 | 단순하고 최적화하기 쉬운 기준선. 관측 대상에 필요한 구역을 정상적으로 선별한다. | +| B: 세밀한 의존성 추적과 결과 보관 | 변경 전파를 활용하는 기준선. 불필요한 객체 할당으로 일부러 느리게 만들지 않는다. | +| C: 묶음별 보관·재계산 선택 | H1을 검증하는 후보. 정책 자체의 시간과 메모리를 포함한다. | +| R: 작업에 맞춰 직접 작성한 네이티브 로직 | 같은 계산의 실용적인 성능 참고점. 라이브 편집 기능이 같지 않다면 별도 표기하며, 수학적인 성능 하한으로 부르지 않는다. | + +A/B/C는 같은 언어, 연산 구현, 상태·출력 의미, 오류 처리와 편집 기능을 사용한다. 주 비교에서 바꾸는 것은 계산·보관 정책이다. 기존 증분 계산 라이브러리와의 비교도 필요하지만, 언어·GC·표현이 함께 달라지는 비교와 정책만 달라지는 비교는 별도로 보고한다. + +작업 부하는 세 종류로 정한다. + +1. 센서와 조건부 입력 연결: 값은 자주 들어오지만 일부 출력만 필요해지는 상황. +2. 목록·대시보드: 일부 항목 수정, 필터 변경, 보이는 영역 전환, 다수 항목의 일괄 갱신. +3. 애니메이션·작은 게임: 많은 상태가 매 처리 단계에서 바뀌는 상황. 앞서 이야기한 공룡 게임은 이 범주의 실사용 검증 사례다. + +노드 수만 늘리는 실험으로 끝내지 않는다. 변경 비율, 출력 요구 비율, 연결의 밀도, 연산 비용, 값의 크기, 편집 빈도를 독립적으로 바꾼다. 긴 연결, 넓은 전파, 빈번한 모드 전환, 느린 출력 소비자를 포함한다. 합성 입력과 실제 조작에서 얻은 입력 기록을 구분한다. + +정책을 조정하는 입력과 최종 평가 입력을 분리한다. 개발 중 유리했던 입력만 최종 결과에 남기지 않는다. + +## 측정 계약 + +핵심 목표는 메모리와 지연의 관계를 측정하는 것이다. 같은 메모리 예산에서의 지연, 같은 지연 한도에서의 메모리를 각각 비교한다. 목표 한도와 유의미한 개선 폭은 기준선과 측정 노이즈를 확인한 뒤, 후보 정책의 최종 평가 전에 고정한다. + +| 대상 | 기록할 값 | +|---|---| +| 코어 메모리 | 모델, 현재 상태, 중간 결과, 의존성·정책 정보, 입력·출력 큐, 임시 공간의 사용량과 확보 용량, 피크 | +| 장시간 보관 | 시간에 따른 살아 있는 바이트 수, 원본 버퍼를 붙잡는 참조의 수명, 가능한 경우 바이트×시간의 누적량 | +| 지연 | 입력 도착부터 결과까지의 P50/P95/P99, 최악 관측값, 규칙 변경의 적용 지연 | +| 실행 비용 | 실행한 연산 수, 추적·무효화·정책 선택·재계산에 쓴 시간, 할당량과 계측한 복사량 | +| 시스템 전체 | 바이너리·코드 크기, 프로세스 메모리, 편집 도구 메모리, 그래픽 자원, 시작 비용 | + +코어가 관리하는 바이트와 OS가 보고하는 상주 메모리는 서로 다른 측정값이다. CPU/GPU가 같은 메모리를 공유하는 환경에서는 같은 자원을 두 번 더하지 않는다. 실행 전용, 편집기 연결, 반복 편집 중의 결과를 각각 남긴다. 편집용 정보를 다른 프로세스나 디스크로 옮겼다면 그 공간과 다시 가져오는 시간도 기록한다. + +수동으로 감싼 복사 함수의 카운터를 모든 기계어 메모리 이동의 추적으로 표현하지 않는다. 계측 도구 자체의 메모리와 시간도 확인한다. + +반응 지연을 측정할 때 입력 발생 시점을 기록해 큐 대기 시간을 포함한다. 느린 실행기에 맞춰 입력 생성까지 느려지는 실험만 하면 실제 지연이 가려질 수 있다. 완료·거절·누락된 작업을 모두 집계한다. 백분위수는 충분한 표본과 반복 실행으로 보고하며 실시간 보장으로 과장하지 않는다. + +실험에 사용한 저장소 SHA, 컴파일러 버전, 최적화 설정, OS, CPU, 전원 상태, 입력 시드, 준비 구간, 반복 횟수를 저장한다. 초기 실행과 준비된 상태를 따로 보고하고 측정 순서를 섞어 열·순서 영향을 확인한다. + +사용자의 M3 macOS를 첫 실측 대상으로 제안한다. 현재 문서 작성 환경에서 M3 성능을 측정한 것은 아니다. 다른 ISA·OS와 임베디드에 대한 주장은 해당 타깃에서 확인한 뒤에 한다. + +## 실패 조건과 다음 결정 + +- 관리 정보까지 더하면 메모리 절감이 사라진다: 표현 또는 정책을 수정하거나 H1을 기각한다. +- 평균은 좋아져도 입력·편집 P99가 정한 한도를 넘는다: 해당 사용 조건에서 실패로 기록한다. +- 제한된 합성 입력에서만 이긴다: 적용 범위를 좁혀 보고하고 일반적인 런타임의 우월성을 주장하지 않는다. +- 단순 고정 정책이 같은 결과를 낸다: 적응형 알고리즘의 추가 비용을 정당화하지 않는다. +- 실행기는 개선되지만 앱 전체의 시간·메모리에 변화가 작다: 다음 연구에서 더 큰 비용을 차지하는 경로를 선택한다. +- 편집 기능을 끄거나 의미를 줄여야만 이긴다: 그 제한을 결과에 명시한다. +- 동일한 정책과 조건의 기존 연구가 확인된다: 기여를 재현·이식·개선으로 정확히 분류한다. + +## 첫 연구 주기의 산출물 + +1. **문헌 비교표 보강:** 위 연구와 후속 연구의 실행 모델, 유지 정보, 변경 지원, 메모리 측정 범위, 평가 조건을 정리한다. 증분 계산·캐시·재계산의 메모리 관리 분야를 우선 조사한다. +2. **기준 실행기와 입력 기록:** 상태 전이와 편집 의미를 고정하고 A/B를 구현한다. 첫 결과는 A/B가 각각 유리한 조건을 재현하는 것이다. +3. **비용 지도:** 어떤 데이터와 관리 정보가 얼마의 공간을 차지하는지, 변경 비율에 따라 시간이 어디에 쓰이는지 공개한다. 여기서 지배적인 비용이 다르면 연구 질문을 갱신한다. +4. **후보 정책과 구성 요소별 실험:** C를 구현하고, 보관 정책·묶음 크기·추적 방법을 각각 바꿔 원인을 설명한다. JIT·SIMD는 이 단계의 프로파일에서 정당화되는 경로에 적용한다. +5. **실사용 연결:** 같은 실행기를 TUI와 하나의 GUI 출력에 연결해 지연·메모리·변경 경험을 확인한다. 코어 결과가 전체 경험으로 이어지는지 측정한다. +6. **재현 패키지:** 실행 명령, 고정한 버전, 입력, 원시 결과, 분석 코드, 유리한 조건과 불리한 조건을 함께 남긴다. + +초기 문헌 조사에 시간 상한을 두고 위 주기를 반복한다. 모든 저수준 분야를 먼저 공부하는 방식으로 확장하지 않는다. 각 주기에서 필요한 이론은 해당 실험과 함께 학습한다. 첫 주기의 핵심은 자료의 수명, 의존 관계, 증분 계산, 측정 통계다. + +## 제작 경험의 별도 연구 + +실행 효율 연구와 함께, 제작 경험에는 다음 질문을 유지한다. + +> 대상·상태·동작·연결을 직접 편집하는 모델이, 기능을 추가하고 고치는 데 필요한 표현과 판단을 얼마나 줄이는가? + +GUI와 터미널이 같은 변경 연산을 사용하게 한다. 공룡 게임의 점프 조건 변경, 센서 연결 교체, 목록의 필터 규칙 변경처럼 완료 조건이 분명한 작업을 선정한다. 작업 완료 시간, 오류, 수정 지점, 모델을 우회한 코드, 학습 시간을 기록한다. AI 사용 조건도 비교에서 맞춘다. + +개발자 본인에게 익숙한 도구가 빠른 효과와 모델 자체의 효과를 구분하려면 이후 외부 사용자 평가가 필요하다. 실행 효율 연구가 성공해도 이 가설의 성공이 자동으로 따라오지 않는다. + +Pure의 연구 결과는 문제와 가설, 보존하는 동작, 바뀐 메커니즘, 비교 결과, 성립 조건까지 연결해 설명할 수 있어야 한다. 이 문서는 그 첫 질문을 선택하기 위한 초안이다. diff --git a/docs/research/002-checkpoint-results.md b/docs/research/002-checkpoint-results.md new file mode 100644 index 0000000..88c1354 --- /dev/null +++ b/docs/research/002-checkpoint-results.md @@ -0,0 +1,174 @@ +# Pure 연구 보고서 002: 중간 결과를 얼마나 보관해야 하는가 + +작성·측정일: 2026-09-22 +상태: **첫 합성 실험 완료. 제품 성능이나 새로운 알고리즘의 입증은 아님.** + +후속: [실행 중 규칙 변경 데모와 보관 공간 보고서](003-live-rules.md)를 추가했다. + +## 먼저 읽을 내용 + +중간 계산 결과를 모두 기억하면 수정 후에 다시 계산할 일이 줄어든다. +일부만 기억하면 메모리를 아끼지만, 수정한 위치에 따라 더 많이 계산해야 한다. +이번에는 이 관계를 확인할 작은 실행기와 재현 가능한 측정 도구를 만들었다. + +16,384개 연산으로 구성한 모델에서, 결과를 매 연산마다 저장하던 것을 +16개마다 저장하도록 바꾸자 **코어의 지속 보관 공간이 262,456바이트에서 +139,576바이트로 46.8% 줄었다.** 이는 자료 구조의 크기를 합산한 값이다. +프로세스 전체 RAM을 측정한 값은 아니다. + +수정이 끝부분에 집중되면 처리 시간은 **67.03ns에서 88.98ns로 약 33% 늘었다.** +다른 수정 패턴에서는 두 방식의 시간이 비슷하거나, 덜 저장하는 쪽이 짧았다. +즉, 메모리를 줄이는 것과 모든 작업을 빠르게 하는 것은 각각 확인해야 한다. + +이 차이가 사용자가 느끼는 반응 속도에 어떤 영향을 주는지는 아직 모른다. +위 숫자는 화면·입력 큐·편집기가 없는 작은 계산 실험의 처리 시간이다. +약 22ns의 차이 자체를 사용자가 느끼는 지연으로 해석하면 안 된다. + +이번 결정은 **저장 간격을 제품 기본값으로 정하지 않고, 비교 가능한 선택지로 +유지한다**는 것이다. 실제 앱에서 어떤 정보를 보관해야 하는지 먼저 확인한다. + +## 사용자가 확인할 경험 + +이번 산출물은 명령행 실험이어서 아직 직접 조작할 화면은 없다. +지금 사용자에게 저수준 실행 방식의 선택이나 성능 수치의 해석을 요구하지 않는다. + +다음 사용 경험 검증의 첫 작업은 앞서 제안한 작은 공룡 게임을 실행하면서 +점프 조건을 바꾸고, 바꾼 규칙과 현재 상태를 확인하는 것이다. +"바꾼 곳을 찾기 어려웠다", "수정하자 진행 상태가 사라졌다", +"왜 점프하지 않는지 확인할 수 없었다"처럼 구체적인 관찰을 받는다. +이를 위해 무엇을 상주시켜야 하는지, 무엇을 다시 계산할 수 있는지 분석하는 일은 +Codex가 맡는다. 이번에 이 데모까지 구현한 것은 아니다. + +## 무엇을 비교했는가 + +서로 연결되지 않은 16개의 계산 사슬을 만들었다. 각 사슬은 앞 연산의 결과를 +다음 연산이 사용한다. 길이는 256개와 1,024개를 각각 시험했다. +연산은 덧셈·XOR·홀수 곱셈·회전/XOR이며, 값은 64비트 부호 없는 정수다. +오버플로는 정해진 정수 규칙으로 처리한다. 관측하는 값은 각 사슬의 최종 결과다. + +연산의 종류·상수와 최초 입력은 실행 중 바꿀 수 있다. 연결 구조는 고정이다. +이는 앞선 연구 의제 전체를 검증하기 전에 계산·보관 비용만 분리한 실험이다. +상태 전이, 그래프 연결 편집, 센서, 외부 출력은 아직 포함하지 않았다. + +| 방식 | 기억하는 결과 | 수정 시 하는 일 | +|---|---|---| +| 전체 재계산 | 사슬의 최종 결과만 | 변경된 사슬을 처음부터 계산 | +| 매 연산 저장 | 모든 연산의 결과 | 첫 변경 지점부터 필요한 부분 계산 | +| 일정 간격 저장 | 4·16·64개 연산마다 결과 | 첫 변경 지점 앞의 저장 위치에서 계산 재개 | + +모든 방식이 같은 모델·연산·변경 기록을 사용한다. 변경되지 않은 사슬은 모두 +건너뛴다. 저장된 값과 같아져도, 같은 처리 단계에 적용된 마지막 변경 지점을 +지나기 전에는 계산을 멈추지 않는다. 전체 재계산과 매 연산 저장에는 전용 반복문을 +사용해, 저장 간격을 처리하는 일반 반복문의 비용을 불필요하게 부과하지 않았다. + +이 실험은 C17로 작성한 측정 장치다. Pure 제품의 언어를 Zig에서 C로 바꾸는 결정은 아니다. + +## 공간 결과: 캐시와 전체 보관 공간을 구분한다 + +아래는 사슬 16개 × 길이 1,024개인 경우다. KiB는 1,024바이트다. + +| 저장 간격 | 결과 캐시 | 코어 지속 보관 공간 | 매 연산 저장 대비 감소 | +|---|---:|---:|---:| +| 1개 | 128 KiB | 262,456 B | 기준 | +| 4개 | 32 KiB | 164,152 B | 37.5% | +| 16개 | 8 KiB | 139,576 B | 46.8% | +| 64개 | 2 KiB | 133,432 B | 49.2% | +| 1,024개: 최종 결과만 | 0.125 KiB | 131,512 B | 49.9% | + +16개마다 저장하면 캐시는 16분의 1이 된다. 하지만 연산 정의 131,072바이트, +입력·변경 범위 256바이트, 실행기 구조체 56바이트는 그대로 필요하다. +따라서 전체 보관 공간은 16분의 1이 되지 않는다. + +측정한 공간은 실행기 구조체 크기와 살아 있는 코어 배열의 요청 크기를 합산한 것이다. +계산식과 실제 할당 배열은 [실험 설명](../../research/experiments/001-checkpoint-granularity/README.md)에 있다. +할당자 내부 정보, 호출 스택, 실행 파일, OS 페이지는 포함하지 않았다. +별도로 미리 만든 변경 기록도 제외하며, 기록 크기는 작업에 따라 +4,112·65,552·524,304바이트였다. 이 제외 항목은 모든 방식에 같은 조건으로 적용했다. +전체 프로세스 메모리와 최대 메모리는 후속 측정 대상이다. + +## 시간 결과: 수정 위치에 따라 달라진다 + +아래도 사슬 16개 × 길이 1,024개다. 단위는 **한 변경 묶음 처리당 마이크로초(µs)**다. +각 숫자는 7회 측정에서 얻은 묶음 평균 처리 시간의 중앙값이며, 입력 지연의 +P50이나 P99가 아니다. 비교가 쉽도록 전체 재계산·매 연산 저장·간격 16을 나란히 적었다. + +| 변경 패턴 | 전체 재계산 | 매 연산 저장 | 16개마다 저장 | +|---|---:|---:|---:| +| 모든 사슬의 최초 입력 변경 | 107.713 | 115.260 | 112.930 | +| 임의 위치의 연산 하나 변경 | 6.339 | 3.401 | 3.375 | +| 끝 8개 중 연산 하나 변경 | 6.295 | 0.067 | 0.089 | +| 사슬마다 연산 8개 변경 | 115.972 | 110.094 | 113.426 | + +끝부분 수정에서 매 연산 저장은 평균 약 4.55개 연산을 다시 실행했고, +간격 16은 16개, 전체 재계산은 1,024개를 실행했다. 처리 시간 차이와 함께 +재계산량 증가를 직접 확인한 경우다. + +반대로 최초 입력이 모두 바뀌면 세 방식이 같은 16,384개 연산을 실행한다. +저장과 비교, 반복문 구조 등의 비용이 달라질 수 있다. 어느 비용이 시간 차이의 +주원인인지는 아직 프로파일링하지 않았다. + +길이 256에서 간격 16은 최초 입력 변경을 약 13.4% 짧은 시간에 처리했지만, +끝부분 수정에는 약 28.2% 더 걸렸다. 길이 1,024에서 관측한 1~3% 수준 차이 중에는 +반복 측정 범위가 겹치는 것도 있어 안정적인 속도 개선으로 결론 내리지 않는다. +간격 4는 두 크기의 네 작업 모두에서 매 연산 저장보다 느린 중앙값을 보였다. +보관 공간은 간격이 커질수록 줄었지만, 처리 시간은 단순히 같은 방향으로 움직이지 않았다. + +선택한 예시 외의 모든 조건과 최소·최대값은 +[전체 요약 표](../../research/experiments/001-checkpoint-granularity/results/2026-09-22-linux-aarch64/summary.csv)에 남겼다. + +## 검증과 재현 + +- 기준 구현은 각 사슬을 처음부터 계산한다. 별도 정확성 검사에서 매 변경 단계의 + 모든 출력을 기준 구현과 비교했다. +- 여러 시드, 길이 1과 홀수 길이, 한꺼번에 적용하는 변경, 아무 변화 없는 입력, + 서로 취소되는 변경, 앞의 변경은 취소되지만 뒤의 변경은 남는 경우를 검사했다. +- 최적화 빌드와 ASan·UBSan 빌드에서 **각각 108,000개의 변경 단계 검사**를 통과했다. + 이는 검사를 통과했다는 뜻이며, 모든 가능한 프로그램의 정확성 증명은 아니다. +- 성능 측정은 40개 조건 × 7회, 원시 표본 280개다. 각 측정 후 기준 출력 검사를 + 수행했고, 같은 크기·작업의 방식 간 최종 출력 체크섬도 일치했다. +- 초기 생성·기록 생성·준비 실행·출력 검사는 시간에서 제외했다. + 변경 적용, 실행 대상 선별, 재계산은 포함했다. 측정 구간 내 동적 할당은 없다. + +호스트는 **Raspberry Pi 5 / Linux aarch64**, 컴파일러는 GCC 14.2.0, 최적화는 `-O3`다. +한 CPU에 실행을 고정하고 측정 순서를 섞었다. 공유 호스트이며 전원·온도는 통제하지 않았다. +256개 처리 단계의 변경 기록을 반복 재생한 준비된 상태의 실험이다. 실제 사람의 입력 분포, +초기 실행, 장시간 실행과 다른 시드에 대한 성능 평가는 별도로 필요하다. +**사용자의 M3 macOS에서 측정한 결과가 아니다.** + +소스와 실행 스크립트의 해시, 컴파일러·호스트 정보, 검사 통과 결과는 +[실행 환경 기록](../../research/experiments/001-checkpoint-granularity/results/2026-09-22-linux-aarch64/environment.json)에 있다. +[원시 결과](../../research/experiments/001-checkpoint-granularity/results/2026-09-22-linux-aarch64/raw.csv)와 +[실행 방법·소스](../../research/experiments/001-checkpoint-granularity/README.md)를 함께 보관했다. +보고서 작성 시 현재 소스·스크립트의 해시가 측정 기록과 같은지 확인했다. + +## 선행연구와 이번 결과의 위치 + +계산을 어떤 크기의 단위로 재사용하는지가 성능에 영향을 준다는 문제는 기존 연구에 있다. +Acar 등의 논문은 의존 관계와 메모이제이션이 재사용하는 단위의 제약을 분석한다. +이번 고정 간격 실험이 그 논문의 알고리즘을 재현한 것은 아니다. +[An Experimental Analysis of Self-Adjusting Computation, TOPLAS 2009](https://www.cs.cmu.edu/~blelloch/papers/ABBHT09.pdf) + +중간 결과를 버렸다가 다시 계산해 메모리 예산을 맞추는 온라인 정책도 기존 연구에 있다. +DTR은 이를 텐서 계산에서 다룬다. 작은 스칼라 연산으로 된 이번 실험에 그 성능 결과를 +옮겨 적용할 수는 없다. +[Dynamic Tensor Rematerialization, ICLR 2021](https://arxiv.org/abs/2006.09616v4) + +따라서 이번 결과는 **알려진 공간·시간 관계를 Pure의 첫 측정 장치에서 확인한 것**이다. +독창적인 기여는 아직 입증하지 않았다. 문헌의 관련 부분을 확인했지만, 후속 연구를 +망라한 체계적 조사나 해당 논문들의 실험 재현을 완료한 상태도 아니다. + +## 다음 결정 + +단순한 사슬은 연결 정보가 암묵적이고, 최종 결과만 필요하다. 실제 앱에서는 하나의 값이 +여러 곳에 쓰이고 편집기가 중간 값을 관찰할 수도 있다. 이때 연결·관찰·편집 정보가 +이번 절감량을 얼마나 상쇄하는지가 다음 질문이다. + +1. 사용 경험 검증용 작은 앱의 모델과 변경 의미를 먼저 구체화한다. + 실행 중 규칙 변경과 현재 상태 관찰에 필요한 정보를 기록한다. +2. 그 모델의 분기·합류와 중간 값 관찰을 비교 실행기에 넣는다. + 보관 공간을 모델·필수 상태·연결·관찰 정보·캐시로 나누어 측정한다. +3. 실제로 지배적인 비용을 확인한 후 저장 정책을 선택한다. + 고정 간격으로 충분하면 자동 적응 정책의 비용을 추가할 이유가 없다. + +초기 의제의 H1인 "변경 특성에 적응하는 정책이 더 유리하다"는 이번 실험으로 +채택하거나 기각할 수 없다. 이번에는 고정 간격만 비교했다. diff --git a/docs/research/003-live-rules.md b/docs/research/003-live-rules.md new file mode 100644 index 0000000..b7c0349 --- /dev/null +++ b/docs/research/003-live-rules.md @@ -0,0 +1,139 @@ +# Pure 연구 보고서 003: 실행 중 규칙을 바꾸고 이유를 확인하기 + +작성일: 2026-09-22 +상태: **구현 검증 완료. 사용자 피드백 반영: 개발 경험 평가에는 부적합.** + +사용자는 이 Dino를 직접 개발하지 않았으므로 개발 경험을 평가할 수 없다고 지적했다. +이 데모로 확인한 범위는 정해진 규칙 변경·상태 보존·판정 표시다. +아래 기존 검사와 수치는 그 범위의 기록으로 보존한다. +새 동작을 직접 작성하는 평가는 [별도 제작 실험 004](004-authoring.md)로 옮겼다. + +## 먼저 직접 해볼 일 + +공룡이 점프하는 작은 연습 장면을 만들었다. 공중에서 두 번째 점프가 거절되면, +그 자리에서 규칙을 **두 번 점프**로 바꾸고 다시 시도할 수 있다. +규칙을 적용해도 위치·속도·진행 상태는 유지된다. 부딪혀도 연습은 계속된다. + +[실행 안내](../../prototypes/live-rules/README.md)에 따라 화면을 열고 다음을 확인하면 된다. + +1. 점프한 뒤 공중에서 다시 점프해, 거절 이유를 찾는다. +2. 공중에서 일시정지하고 **두 번 점프**로 바꿔 **규칙 적용**을 누른다. +3. 위치와 진행 상태가 유지되는지 보고 다시 점프한다. +4. 점프를 비활성화한 뒤, 무엇 때문에 입력이 거절됐는지 확인한다. + +시간을 맞추기 어려우면 정지 상태에서 점프하고 한 프레임씩 진행해도 된다. +정지 중에도 입력은 판정하지만 위치는 프레임을 진행할 때 움직인다. +**처음부터**는 규칙과 진행 상태를 함께 초기화한다. + +사용자가 알려줄 것은 “하려던 일 / 실제로 일어난 일 / 기대했던 일”이다. +예를 들어 “규칙을 바꿨는데 여전히 거절 표시가 보여 적용 실패로 생각했다”는 관찰이면 +충분하다. 그 원인을 재현하고, 화면과 실행 의미 중 무엇을 고칠지 분석하는 일은 Codex가 맡는다. + +## 무엇을 구현했는가 + +Zig로 작성한 코어가 점프 판정, 물리, 규칙 변경, 진행 상태와 사건 기록을 담당한다. +이를 WebAssembly로 빌드해 브라우저와 터미널에서 사용한다. 두 어댑터는 같은 Wasm 파일을 +각자의 인스턴스로 실행한다. 현재 두 화면의 진행 상태를 실시간으로 공유하는 기능은 없다. + +판정은 입력 요청·활성 여부·접지 여부·남은 점프 수를 읽고, 조건을 선택해 합류하는 +**고정된 여섯 노드 그래프**다. 화면에서 보는 노드 값은 코어가 실제 판정에 사용한 값이다. +바꿀 수 있는 것은 점프 조건·힘·활성화 세 항목이며, 모두 검증한 뒤 함께 적용한다. +지원하지 않는 값은 기존 규칙과 상태를 유지한 채 거절한다. + +연산이나 연결을 자유롭게 추가하는 편집기, 임의 프로그램의 상태 이전은 아직 구현하지 않았다. +이번에는 앞선 계산 사슬과 별도의 상호작용 모델에서 편집과 관찰의 의미를 확인했다. +새로운 알고리즘이나 일반 런타임의 성능 우월성을 입증한 결과는 아니다. + +## 규칙을 바꿨는데 왜 이전 판정이 남는가 + +판정 화면은 **마지막 입력을 처리하기 직전의 상태와 그때 사용한 규칙 버전**을 보여준다. +규칙을 수정했다고 과거의 입력이 새 규칙으로 처리된 것은 아니므로, 이 기록도 그대로 둔다. +다음 입력이 들어오면 새 규칙으로 판정하고 화면의 기록을 교체한다. + +따라서 현재 규칙이 버전 2이고 마지막 판정이 버전 1인 상태는 정상이다. +사용자가 이 차이를 이해할 수 있는지는 실제 조작으로 확인해야 한다. +기록을 더 보관하는 것만으로 설명이 쉬워진다고 가정하지 않는다. + +## 보관 공간에서 확인한 사실 + +아래 값은 Zig의 `@sizeOf`로 얻은 **선언된 자료 구조의 크기**다. + +| 항목 | 바이트 | 포함하는 정보 | +|---|---:|---| +| 규칙 | 16 | 조건·힘·활성화·버전 | +| 진행 상태 | 68 | 위치·속도·장애물·횟수 등 | +| 마지막 판정 | 24 | 판정 당시 상태·버전·여섯 노드 값 | +| 사건 기록 | 264 | 최근 사건 16개와 기록 위치 | +| 코어 구조체 | 372 | 위 네 항목을 포함한 크기 | +| 그래프 정의 | 24 | 여섯 노드의 고정 정의 | +| 선언 크기 합계 | **396** | 코어 구조체 + 그래프 정의 | + +마지막 판정과 사건 기록은 합쳐서 288바이트다. 이 작은 모델에서는 전체 선언 크기 +396바이트 중 약 72.7%를 차지한다. **사용자에게 실행 이유를 보여주기 위한 정보도 +따로 계산해야 한다**는 구체적인 사례다. 다른 앱에서도 같은 비중이라는 뜻은 아니다. + +이 합계는 실제 RAM 사용량이 아니다. 그래프 상수는 컴파일러가 명령어로 접을 수 있고, +Wasm 엔진과 화면에도 별도 비용이 있다. 코어 구조체는 앞의 네 항목을 포함하므로 +표의 모든 행을 더하면 중복 계산이 된다. + +실제 Wasm 선형 메모리의 용량은 **65,536바이트(64 KiB)**로, 초기값과 최대값이 같다. +코어 데이터와 예약한 16 KiB 스택은 이 공간 안에 포함된다. +선언 크기나 스택을 64 KiB에 다시 더하지 않는다. +빌드된 Wasm 파일은 **32,544바이트**이며, 파일 크기와 메모리 용량도 서로 다른 값이다. +브라우저·Node 엔진, DOM·렌더러, 실행 코드와 프로세스 전체 메모리는 이번 수치에 포함하지 않았다. + +최근 사건 16개를 넘으면 오래된 사건을 덮어쓴다. 이것은 완전한 실행 재생 로그가 아니다. +별도의 정해진 명령열로 재생 검사를 수행하며, 메모리에 모든 과거 상태를 보관하지 않는다. + +## 검증과 증거 + +- 네이티브 Zig 검사 **11개**를 통과했다. 규칙 변경 시 상태 보존, 잘못된 변경의 거절, + 정지·한 프레임 진행, 두 번 점프, 그래프 판정, 기록 한도, 재생 일치와 카운터 처리를 검사했다. +- 실제 Wasm을 사용하는 어댑터 검사 **6개**를 통과했다. 같은 입력 의미, 실행 중 변경, + 잘못된 숫자 입력, 반복 실행 후의 기록·메모리 한도와 재생 결과를 확인했다. +- 별도 코드 검토에서 코어와 어댑터의 수정 원자성, 범위 검사, 공간 집계 표현을 확인했다. + 터미널에 소수·범위를 넘는 정수·음수·지수 표기를 넣었을 때 거절되고 기존 상태가 유지되는지도 확인했다. + +검사를 통과했다는 것은 다룬 시나리오의 결과이며 모든 가능한 실행의 정확성 증명은 아니다. +검증 환경은 Linux aarch64, Zig 0.15.2와 Node 24다. **M3 macOS 측정은 수행하지 않았다.** +처리 속도, 사용자 입력 지연과 전체 앱 메모리를 비교하는 벤치마크도 수행하지 않았다. + +재현 명령은 [실행·검증 안내](../../prototypes/live-rules/README.md)에 있다. +소스·Wasm 해시, 구조체 크기와 명령별 상태를 담는 +[실행 기록](../../prototypes/live-rules/results/2026-09-22-linux-aarch64/storage-and-replay.json)은 +최종 소스와 대조해 저장했다. 이번 기록에는 네이티브 검사 결과와 빌드 정보도 포함한다. +향후 재실행에서 빌드 기록이 없으면 컴파일러 출처를 확인한 것으로 취급하지 않는다. + +## 실제 브라우저 조작 검증 + +Chromium 151과 Playwright CLI 0.1.21에서 실제 컨트롤을 조작해 **23개 확인 항목**을 통과했다. +JavaScript로 코어 상태를 직접 바꾸는 방식 대신 버튼·키보드·폼을 사용했고, +읽기 전용 관찰 함수로 결과를 확인했다. + +- 정지·실행·한 프레임 진행, 키보드와 버튼 입력, 키 반복과 중복 입력 방지를 확인했다. +- 초안은 적용 전까지 실행에 영향을 주지 않았고, 적용 시점의 높이 60.20px, 속도 5.60px/틱, + 진행 거리 32px, 처리 단계 8이 모두 그대로 유지됐다. 다음 점프는 새 규칙으로 허용됐다. +- 이전 버전 판정 표시, 세 번째 점프와 비활성화된 점프의 거절, 명시적 초기화를 확인했다. +- 최대 세기의 두 번 점프가 화면 밖으로 사라지지 않았다. 390px 폭에서 가로 넘침 없이 + 컨트롤을 조작했고, 공룡이 너무 작아 보이는 초기 표시를 확대해 확인했다. +- Wasm 파일을 불러오지 못할 때 실행 명령 안내와 비활성화된 컨트롤을 확인했다. + +최종 소스·Wasm 해시와 상태 전후 값은 +[브라우저 검증 기록](../../prototypes/live-rules/results/2026-09-22-linux-aarch64/browser-checks.json)에 있다. +[데스크톱 화면](../../prototypes/live-rules/results/2026-09-22-linux-aarch64/desktop.png)과 +[좁은 화면](../../prototypes/live-rules/results/2026-09-22-linux-aarch64/mobile.png)도 보관했다. +좁은 화면 검사는 데스크톱 브라우저의 뷰포트 검사이며 실제 휴대폰이나 터치 센서 검증은 아니다. +이 검증은 동작과 표시를 확인한 것이며, 사용자가 쉽게 이해하는지에 대한 평가를 대신하지 않는다. + +## 다음에 판단할 질문 + +아래는 당시의 후속 질문이다. 이후 사용자 피드백으로 **빈 작업물에서 직접 만드는 과정**을 +먼저 확보하기로 바꿨다. 이 데모의 평가를 다시 요구하지 않는다. + +먼저 사용자가 “왜 입력이 거절됐는지”와 “변경이 언제 적용됐는지”를 이 화면에서 +설명할 수 있는지 확인한다. 찾기 어렵다면 어떤 정보나 표현이 부족한지 재현한다. + +그다음 이 작은 모델에서 288바이트를 쓰는 판정·사건 기록 중 **항상 남겨야 할 정보와 +필요할 때 다시 계산할 수 있는 정보**를 나눈다. 이전 판정을 정확히 다시 설명하려면 +당시 상태와 규칙의 어떤 부분이 필요한지부터 확인해야 한다. +사용자가 실제로 찾는 정보와 정확성을 유지하는 조건이 정해진 뒤 보관·재계산 방식을 비교한다. diff --git a/docs/research/004-authoring.md b/docs/research/004-authoring.md new file mode 100644 index 0000000..6ced695 --- /dev/null +++ b/docs/research/004-authoring.md @@ -0,0 +1,176 @@ +# Pure 연구 보고서 004: 빈 작업물에서 직접 작성하기 + +작성일: 2026-09-22 + +**이번에는 빈 문서에서 상태·입력·도형·반응을 작성하는 도구를 구현했다.** +새 동작을 추가하는 데 실행기 내부를 수정하지 않아도 된다. 자동 검사와 실제 브라우저 조작은 +통과했지만, 사용자가 배우기 쉬운지와 기존 코드보다 제작하기 편한지는 아직 모른다. +실행 비용을 비교한 현재 구현은 고정된 JavaScript 예제보다 느렸다. + +후속 사용자 관찰: 문법은 매력적이고 기본 사용에 걸림돌이 없었지만, 현재 결과는 작은 DSL이며 +실제 큰 서비스·앱·CLI를 개발해야 가치를 판단할 수 있다는 피드백을 받았다. +이 관찰은 사용자 한 명의 현재 경험이다. 대규모 제작·성능·독창성은 여전히 미검증이다. +다음 설계는 [기반 계약 보고서 005](005-foundation-contract.md)에 기록했다. + +[제작 화면 실행 안내](../../prototypes/workbench/README.md)를 따라 자신의 작은 동작을 만들어볼 수 있다. +첫 화면은 비어 있고, 예제는 직접 선택했을 때만 편집기에 들어온다. +사용자가 전달할 관찰은 **하려던 일 / 실제로 일어난 일 / 기대했던 일**이면 된다. +도구의 표현이나 설명이 막히는 지점을 재현하고 개선하는 일은 Codex가 맡는다. + +## 이전 실험에서 바로잡은 점 + +사용자는 “Dino를 직접 만든 것이 아니므로 개발 경험을 평가할 수 없다”고 지적했다. +맞는 지적이다. 기존 데모는 정해진 동작의 설정 변경과 상태 보존을 시험했으며, +새 동작을 만드는 과정은 제공하지 않았다. 그 결과를 개발 경험의 증거로 쓰지 않는다. + +이번 실험에서는 사용자가 직접 다음을 정의한다. + +| 작성할 것 | 표현 | +|---|---| +| 기억할 값 | `state x = 80` | +| 입력 이름과 키 | `input right = ArrowRight` | +| 보여줄 대상과 상태 연결 | `circle seed(x, 180, 20, "#7ed9ae")` | +| 누름·놓음·시간 반응 | `on right.down`, `on right.up`, `on frame` | +| 계산·조건 | 대입, 산술·비교, `if`, `min/max/clamp` | + +점프·중력·충전·충돌은 코어에 내장하지 않았다. 충전 예제의 중력과 바닥 판정도 작성한 코드에 있다. +화면의 입력 버튼, 상태 목록과 도형은 선언한 내용에서 만들어진다. +현재 범위는 숫자 상태와 간단한 도형을 사용하는 작은 상호작용이다. 임의 앱, 센서, +자유로운 창 관리, 셰이더, 문자열·배열·함수 등을 지원한다는 뜻은 아니다. + +## 실제 제작 과정에서 확인한 의미 + +편집 중인 코드와 실행에 적용한 코드를 구분한다. 적용할 때 **같은 이름의 숫자 상태**는 유지한다. +상태 선언 순서를 바꿔도 이름으로 대응하며, 새 이름은 초깃값을 사용한다. +이름을 바꾸는 행위의 의미까지 추론하지는 않는다. 초깃값 변경을 현재 값에 반영하려면 초기화를 누른다. + +문법·이름·용량 오류가 있는 수정은 기존 작업을 유지한다. 계산 중 오류가 생기면 해당 이벤트에서 +실행한 모든 반응의 숫자 상태 변경을 함께 취소한다. 키보드와 포인터가 같은 입력을 동시에 +누르면 첫 누름과 마지막 놓음만 전달한다. 누른 상태에서는 새 코드를 적용할 수 없다. + +브라우저 검증 중 서로 다른 도형·입력·반응을 소스만 편집해 추가했다. 기존 실행기 변경은 없었다. +미적용 초안의 저장·불러오기도 확인했다. 실제 사용자의 자유로운 제작 성공률이나 소요 시간은 +이 자동화로 측정되지 않는다. + +## 이번에는 실제로 명령 해석기가 있다 + +초기 대화의 네이티브 C 함수 구성에는 자체 바이트코드 VM이 없었다. +이번에는 작성한 동작을 실행 중 교체하는 실험을 위해 **작은 중간 명령 해석기**를 도입했다. +구성이 달라졌으므로 바이트코드 저장량을 0이라고 말할 수 없다. + +```text +작성한 .pure 텍스트 + → JavaScript 컴파일러: 이름·타입의 범위와 용량 검사, 중간 명령 생성 + → Zig 코어: 중간 명령 실행, 상태·오류·적용 관리 + → 브라우저 SVG 또는 터미널 JSON +``` + +Zig 코어 자체는 WebAssembly로 빌드한다. 사용자가 작성한 중간 명령과 이 Wasm 파일은 +서로 다른 대상이다. 브라우저와 터미널은 같은 컴파일러·코어를 사용하지만 별도 인스턴스다. +이 선택이 최종 제품의 언어·컴파일 방식으로 확정된 것은 아니다. + +## 보관 공간과 실행 명령 수 + +[충전 예제](../../prototypes/workbench/examples/charge.pure)를 실제 컴파일·실행한 값이다. +소스는 674바이트, 상태 4개, 입력 1개, 출력 4개, 반응 3개이며 중간 명령은 66개다. + +| 항목 | 바이트 | 의미 | +|---|---:|---| +| 사용 중인 명령·반응 정보 | **1,092** | 명령 66 × 16 + 반응 3 × 12; 표시 계산 명령 포함 | +| 현재 숫자 상태 값 | **32** | 4 × 8 | +| 프로그램 한 개의 예약 공간 | 9,392 | 명령·반응·초깃값 등의 고정 용량 | +| 코어 구조체 예약 공간 | **19,072** | 실행 중 프로그램, 다음 적용을 위한 준비 공간, 상태·관리 정보 포함 | +| 그중 다음 프로그램 준비 공간 | 9,392 | 위 코어 구조체에 포함 | +| 한 번의 계산 평가 스택 용량 | 512 | 64 × 8; 영구 상태와 다른 임시 공간 | +| Wasm 선형 메모리 | **65,536** | 초기·최대값 고정; 코어와 예약한 16 KiB 호출 스택 포함 | +| 배포 Wasm 파일 | **48,182** | `ReleaseSafe`, 디버그 정보 제거한 파일 크기 | + +표의 포함 관계를 무시하고 합산하면 안 된다. 특히 32바이트 상태만으로 전체 프로그램이 +돌아간다는 뜻이 아니다. JS 컴파일러·소스·중간 명령 객체, 브라우저·Node 엔진, SVG, +기계어 코드와 프로세스 전체 메모리는 이 선형 메모리 수치에 포함되지 않는다. +JavaScript 비교 모델의 힙 사용량도 측정하지 않았으므로 메모리 우월성은 판단하지 않는다. + +고정 용량 덕분에 이 코어는 작업이 작아도 프로그램 두 개 분량을 예약한다. +작업에 비례해 메모리가 줄어드는 구현도 아니고, 이 19 KiB를 최적이라고 주장하지도 않는다. + +해당 예제에서 관찰한 이벤트당 중간 명령 실행 수는 다음과 같다. + +| 실제 상황 | 실행한 중간 명령 | +|---|---:| +| 바닥에 정지한 상태로 한 프레임 | 22 | +| 바닥에서 입력 누르기 | 8 | +| 충전 중 한 프레임 | 29 | +| 한 프레임 충전한 뒤 놓기 | 13 | +| 이후 공중에서 한 프레임 | 18 | + +이 값은 **해당 이벤트에서 해석한 명령** 수다. CPU 명령 수·CPU 사이클·함수 호출 횟수가 아니다. +컴파일, 호스트 입력 처리, 표시 표현식 평가와 렌더링도 제외한다. +[빌드·검사·명령 수 기록](../../prototypes/workbench/results/2026-09-22-linux-aarch64/verification.json)에 +당시 상태와 코드·Wasm 해시를 남겼다. + +## 일반 JavaScript와 비교한 결과 + +[독립 JavaScript 모델](../../prototypes/workbench/web/baseline.mjs)은 컴파일러나 Pure 코어를 사용하지 않는다. +같은 충전·해제·중력·바닥 계산과 출력 값을 일반 코드로 구현했다. +이 JS 모델에는 임의 프로그램의 실행 중 교체나 이름에 따른 상태 이전 기능이 없다. + +길거나 짧은 누름, 중복 누름, 두 입력원, 공중 입력, 놓기, 초기화를 포함하는 +**6,663회 호스트 호출**을 양쪽에 재생했다. 각 호출 뒤 상태·출력·틱·누른 입력을 비교했다. +초기 상태 포함 **6,664개 스냅샷**, 숫자 **126,616개**에서 차이가 0이었다. +이는 기록한 실행열에서의 일치다. 모든 가능한 실행의 동등성 증명은 아니다. +예를 들어 코어의 틱 카운터는 32비트에서 순환하지만 JS 모델의 틱은 같은 방식으로 순환하지 않는다. +이번 실행열은 그 경계에 도달하지 않는다. + +같은 실행열로 준비 실행 후 측정 순서를 교대하며 7회 측정했다. +환경은 **Linux aarch64, Cortex-A76, Node 24.20.0, Zig 0.15.2**다. +M3 macOS에서 얻은 결과가 아니다. + +| 경로 | 호출당 시간 중앙값 | 7회 측정값의 범위 | +|---|---:|---:| +| Pure 호스트 + Wasm 명령 해석기 | **569 ns** | 516~735 ns | +| 고정 JavaScript 모델 | **53.4 ns** | 52.7~74.6 ns | + +이 실행열에서는 Pure 경로가 약 **10.7배** 오래 걸렸다. +누름·놓음 처리, 프레임 계산, 초기화 등의 총 시간을 호출 수로 나눈 배치 처리량이다. +한 입력의 지연 분포나 화면 응답 시간은 아니다. Pure 쪽의 일반 명령 해석과 진단 관리 비용도 +함께 들어 있으므로 이 차이를 특정 연산이나 Wasm 경계 하나의 비용으로 단정하지 않는다. +컴파일·표시 표현식·스냅샷 생성·DOM 렌더링·코드 교체 시간은 제외했다. +CPU 고정·전력·온도 통제도 하지 않았으므로 절대 수치를 일반화하지 않는다. + +작성 파일은 `.pure` 34줄·674바이트, JS 모델 68줄·2,251바이트다. +이는 주석·빈 줄을 포함한 물리적 파일 길이이며, 편집기의 줄 수는 마지막 빈 줄까지 세어 다를 수 있다. +JS 모델에는 입력원 관리와 출력 데이터 생성 코드도 들어간다. Pure는 그 일부를 공통 실행기로 +옮겼으며 컴파일러·호스트 코드가 사라진 것이 아니다. 이 길이로 학습 부담이나 개발 시간을 판정하지 않는다. + +재현 가능한 실행열 생성기·모든 측정값·환경·해시는 +[비교 원자료](../../prototypes/workbench/results/2026-09-22-linux-aarch64/comparison.json)에 있다. + +## 검사와 남은 질문 + +- Zig 네이티브 18개: 이벤트 원자성, 범위·스택·분기, 상태 이전, 메모리 집계 등. +- 컴파일러 13개: 문법·이름·식·분기·용량·예제 작성 범위. +- 실제 Wasm 호스트 통합 13개: 새 동작 작성, 입력원 결합, 적용 실패 보존, 상태 이름 대응, + 오류 보존, 표시 계산의 순수성, 별도 인스턴스 일치, 고정 메모리. +- 독립 JS 모델 7개, 실제 터미널 프로세스 4개. +- Chromium 실제 조작 44개: 빈 작업물 작성부터 새 동작 추가, 편집 중 키 입력 격리, + 잘못된 수정·실행 오류, 초안 파일 저장·복원, 390px 화면과 Wasm 로드 실패 처리. + +검사 중 외부에서 반환된 프로그램 정보를 변경하면 다음 적용의 상태 대응이 틀어질 수 있는 문제를 +발견해 해당 정보를 변경 불가능하게 만들었다. 실패한 누름 뒤 정상적인 놓음이 오류를 지워버리는 +문제도 고쳤다. 마지막 실패와 가장 최근 입력을 따로 표시한다. + +[브라우저 증거](../../prototypes/workbench/results/2026-09-22-linux-aarch64/browser-checks.json), +[빈 화면](../../prototypes/workbench/results/2026-09-22-linux-aarch64/blank.png), +[작성한 화면](../../prototypes/workbench/results/2026-09-22-linux-aarch64/authored.png), +[좁은 화면](../../prototypes/workbench/results/2026-09-22-linux-aarch64/mobile.png)을 보관했다. +좁은 화면 검사는 데스크톱 브라우저의 뷰포트 검사이며 실제 휴대폰 센서·터치를 검증하지 않는다. + +다음 사용자 관찰은 **자신이 정한 동작을 이 표현으로 끝까지 작성할 수 있는가**다. +원하는 작은 숫자·도형 동작도 실행기 수정을 요청해야 한다면 그 지점이 표현 범위의 실패다. +구현 세부사항을 이해하지 못했다는 이유로 사용자의 책임으로 돌리지 않는다. + +다음 성능 연구에서는 같은 의미를 유지하면서 실제 사용하는 상태·명령 수가 달라질 때 +예약 공간, 상태 복사, 명령 해석, 호스트 경계, 표시 계산의 비용을 각각 분리해야 한다. +현재 전체 경로의 측정만으로 어느 부분을 최적화해야 할지 확정하지 않는다. +새 원리나 기존 런타임 대비 우월성은 아직 입증하지 않았다. 이번 산출물은 +그 판단에 필요한 제작 경험과 수치를 얻기 위한 실행 가능한 실험이다. diff --git a/docs/research/005-foundation-contract.md b/docs/research/005-foundation-contract.md new file mode 100644 index 0000000..842b732 --- /dev/null +++ b/docs/research/005-foundation-contract.md @@ -0,0 +1,139 @@ +# Pure 연구 보고서 005: 연산·수명·모듈 경계 + +작성일: 2026-09-22 +상태: **설계 초안과 데이터 레이아웃의 제한된 컴파일 검사 완료**. +버퍼 런타임·모듈 로더·포맷 지원·새 컴파일러가 구현됐다는 뜻은 아니다. + +## 이번에 정한 것 + +사용자는 현재 문법이 매력적이고 기본 사용에 걸림돌이 없었다고 평가했다. +동시에 현재 결과는 평범한 작은 DSL로 보이며 실제 서비스·앱·CLI 개발을 통해 가치를 확인해야 한다고 했다. +이 피드백을 특정 사용자의 현재 경험으로 기록한다. 대규모 개발 생산성이나 독창성 검증으로 확대하지 않는다. + +그다음 합의는 **연산 의미 → 데이터 수명·소유권 → 모듈 계약**을 먼저 명세하는 것이다. +커널 우회는 실제 입출력 병목이 확인될 때 별도 백엔드로 검토한다. +압축·암호화·문서·미디어·OS를 다루려는 장기 목표도 기록했다. +각 분야를 실제 지원하거나 새로운 알고리즘을 만드는 일은 별도의 구현·검증 범위를 갖는다. + +이번 산출물: + +| 문서 | 정한 경계 | +|---|---| +| [연산 의미](../design/001-operation-semantics.md) | 숫자 계산, 관리 상태 확정, 버퍼 변환, 외부 효과의 실패 범위 | +| [메모리·수명](../design/002-memory-lifetime.md) | 예산, 버퍼 소유자·대여, 비동기 완료·취소·반납 | +| [모듈 계약](../design/003-module-contract.md) | 프로파일·버전·진행 결과, 네이티브 호출과 영속 형식의 구분 | +| [C17 레코드 초안](../design/abi-v0.h) | 핸들·구간·스트림 결과·한도 정보의 필드 폭과 크기 | + +제작 화면의 문법·동작은 이번 작업에서 바꾸지 않았다. 새 데모를 조작해 평가할 과제도 없다. +현재 사용성 피드백을 유지하면서 다음 실제 앱이 요구할 기반을 명확히 하는 단계다. + +## 가장 중요한 세 가지 결정 + +**첫째, 취소 요청과 버퍼 반납은 다르다.** +파일·네트워크·장치 작업은 취소 요청 뒤에도 접근이 남을 수 있다. +접수된 작업의 대여는 최종 완료를 회수할 때까지 유지한다. 동기 백엔드도 같은 요청 계약을 사용한다. +libuv 역시 요청 관련 메모리를 완료 콜백 전에 해제하지 않도록 요구한다. +이 자료를 일반적인 수명 계약의 근거로 사용했고 libuv를 실제 의존성으로 추가하지는 않았다. +[libuv 요청 계약](https://docs.libuv.org/en/v1.x/request.html#c.uv_cancel) + +**둘째, 입력 소비·유효 출력·스트림 종료를 따로 센다.** +압축 해제는 입력을 다 읽은 뒤에도 출력과 종료 처리가 남을 수 있다. +첫 바이트 프로파일은 `consumed`, `produced`, `status`를 따로 반환한다. +코덱의 내부 윈도·문맥도 예산에 포함해야 하므로 외부 버퍼 크기만으로 전체 메모리를 판단하지 않는다. +근거로 읽은 Zstd 매뉴얼은 1.5.1이며 최신 라이브러리 버전 추천은 아니다. +[Zstandard 매뉴얼](https://facebook.github.io/zstd/zstd_manual.html) + +**셋째, 물리적으로 쓴 바이트와 사용 가능한 데이터는 다르다.** +첫 출력 프로파일은 유효 길이 0인 별도 버퍼에 쓰고, 완료 회수 시 검증된 접두부만 공개한다. +인증 복호화는 해당 인증 단위가 성공한 뒤에만 평문을 유효 출력으로 전달한다. +이것은 검증된 암호 라이브러리의 계약을 보존할 조건이며 자체 암호 알고리즘 제안이 아니다. +[libsodium 인증 암호화](https://doc.libsodium.org/secret-key_cryptography/secretbox) + +## 현재 구현과의 차이 + +현재 Workbench의 숫자 오류 복구는 32개 `f64` 배열을 임시 복사한 뒤 함께 확정하는 구조다. +외부 출력이나 제자리 버퍼 쓰기까지 되돌리는 기능은 없다. +그래서 새 초안은 **자원 예약 → 관리 상태·작업 목록 확정 → 실제 백엔드 제출 → 완료 처리**를 구분한다. +확정 전 자원 부족은 기존 상태를 보존하며, 확정 후 백엔드 실패는 이미 발급한 티켓의 완료로 보고한다. + +현재 `revision`은 적용 횟수이며 ABI 버전이 아니다. 내부 Zig 구조체의 `@sizeOf`도 +포맷·모듈 파일의 직렬화 규칙이 아니다. 64 KiB는 Wasm 선형 메모리이며 전체 컴파일러·앱의 상한이 아니다. +이 세 수치를 미래 계약의 보장으로 잘못 재사용하지 않는다. + +## 작게 만들기 위해 선택한 제약 + +- 첫 모듈은 바이트 스트림 프로파일로 제한한다. 파일·GPU·다중 평면 미디어의 결과는 별도 프로파일이 필요하다. +- 한 스트림 문맥에는 진행 요청 하나만 허용한다. 첫 제어 흐름은 실행 영역별 직렬 처리다. +- 입력과 출력 버퍼를 분리하고 버퍼 전체 단위로 읽기 공유 또는 쓰기 독점을 적용한다. +- 요청·완료·대여·버퍼 슬롯을 함께 예산에 넣는다. 느린 소비자로 포화되면 역압력을 반환한다. +- 진행 작업·대여·열린 문맥이 남아 있으면 교체를 거절한다. 첫 계약은 무중단 모듈 교체를 약속하지 않는다. +- 모듈마다 새 문법·전역 타입 분석·무제한 특수화를 컴파일러에 추가하지 않는다. +- 작은 핵심 계약과 검증된 외부 라이브러리의 연결을 먼저 시험한다. 내부 알고리즘 재구현은 독립적인 비교 근거가 필요하다. + +제약은 비용을 줄이기 위한 가설이지 성능 측정 결과가 아니다. +고정 풀도 미사용 예약 공간을 낭비할 수 있고, 공통 코드도 직접 특수화한 코드보다 느릴 수 있다. +어느 쪽이 유리한지는 실제 작업과 전체 비용으로 비교한다. + +## 장기 분야가 요구하는 검증 + +| 분야 | 공통 계약에 요구하는 것 | 해당 분야의 별도 책임 | +|---|---|---| +| 압축 | 부분 소비·생성, 종료 처리, 내부 메모리 상한 | 포맷 호환, 압축률·속도·입력 분포, 사전·윈도 | +| 암호화 | 출력 공개 시점, 키·임시 버퍼 수명 | 검증된 구현, 키·nonce·인증 단위·프로토콜의 올바른 사용 | +| 문서 | 범위가 명시된 읽기, 점진 처리, 오류 위치 | 구조·인코딩·참조·랜덤 접근·각 포맷의 의미 | +| 미디어 | 버퍼 대여, 비동기 완료, 시간·역압력 | 프레임·평면·코덱·GPU 동기화·실제 시간 제약 | +| OS·장치 | 외부 효과·핸들·완료·어댑터 경계 | 플랫폼 API·권한·드라이버·커널 경로, 자체 OS의 별도 설계 | + +이 표는 모든 분야를 하나의 바이트 함수로 표현할 수 있다는 주장이 아니다. +공통으로 보존해야 할 수명·진행·비용 규칙을 정하고, 의미가 다른 부분은 각 모듈이 책임지게 한다. +지원할 실제 포맷·버전·플랫폼의 목록이나 전체 OS 개발 일정은 아직 정하지 않았다. + +## 검증한 범위와 증거 + +코드·문서 검토에서 다음 모순을 찾아 초안에서 해결했다. + +- 런타임 접수 전 거절과 관리 상태 확정 뒤 백엔드 제출 실패의 혼동. +- 출력 오프셋이 임의일 때 단일 유효 길이로 초기화되지 않은 구간까지 공개할 수 있는 모호함. +- 취소 중 공유 입력 읽기를 허용하는 규칙과 모든 읽기를 금지하는 검사 항목의 충돌. +- 동기 백엔드 반환과 런타임의 완료 회수 중 어느 시점에 대여가 끝나는지의 충돌. + +그 결과 v0 출력은 빈 버퍼·오프셋 0으로 제한하고, 모든 접수 요청의 대여는 완료 회수까지 유지한다. +입력의 공유 읽기는 허용하지만 충돌하는 쓰기·재사용은 거절한다. + +Zig 0.15.2의 C 컴파일러로 **AArch64 Linux, AArch64 macOS, wasm32**를 대상으로 +레코드 크기·오프셋의 `_Static_assert` 8개씩을 컴파일했다. 세 타깃 모두 성공했다. +이는 헤더의 제한된 데이터 배치 검사다. M3 실행·함수 호출 ABI·버퍼 수명·성능 검증이 아니다. +[컴파일 기록과 해시](../design/validation/2026-09-22-record-layout.json)를 보관했다. + +```sh +python3 docs/design/check-abi-layout.py --zig /path/to/zig --output /tmp/pure-abi-new.json +``` + +출력 파일은 덮어쓰지 않는다. native ABI의 다음 검증은 독립 C 생산자와 Zig 소비자 사이의 +실제 호출, 버전 거절, 수명·예산 처리다. 현재 헤더에는 함수 테이블이나 동적 로더 구현이 없다. + +## 다음 구현의 합격 기준 + +2026-09-22 후속 결정: [006의 문제 조사](006-problem-landscape.md)에 따라, +아래 구현에 앞서 기존의 잘 구성한 구현에서 실제 비용과 남은 개선 여지를 확인한다. +이 계약은 후보 설계이며, 조사·비교 결과에 따라 변경한다. + +먼저 작은 고정 버퍼 풀과 요청 슬롯에서 제출·완료·반납을 실행하고, +포화·부분 출력·취소 경합·늦은 완료·오래된 핸들·교체 거절을 재현한다. +이는 수명 계약 검사이며 실제 앱 사용성 평가로 대신하지 않는다. + +그다음 실제 파일 스트림과 검증된 변환 라이브러리를 연결해 +풀의 총 용량보다 큰 파일, 느린 출력, 잘린 입력, 출력 실패를 다룬다. +직접 라이브러리를 호출하는 일반 구현과 결과·오류 의미를 맞춘 뒤 다음을 비교한다. + +| 측정 축 | 기록할 대상 | +|---|---| +| 컴파일러 | 소스·토큰·AST·IR 용량, 최대 작업 메모리, 컴파일 시간, 산출물 크기 | +| 실행 메모리 | 확보·사용·유지 바이트, 내부 문맥·완료 큐·계측 공간, 전체 RSS | +| 복사 | 계측한 경로별 바이트, 원본 버퍼 유지 시간, 측정하지 못한 경로 | +| 처리 | 입출력 총량·정확성·처리량·큐 대기 포함 지연·완료/거절/취소 수 | +| 제작 | 실제 앱에서 새 기능을 위해 계약 밖 코드나 실행기 변경이 필요했던 지점 | + +새 API가 모듈 내부 메모리를 숨기거나, 풀·완료 큐·이전 문맥을 포함하면 이득이 사라지거나, +부분 실패에서 데이터를 잃으면 해당 설계를 수정한다. 기존 구현에 없던 제약을 붙여 이긴 경우에도 +같은 조건의 비교로 별도 보고한다. 이번 초안에서 새로운 원리·성능 우월성을 입증하지 않았다. diff --git a/docs/research/006-problem-landscape.md b/docs/research/006-problem-landscape.md new file mode 100644 index 0000000..cbfd6b7 --- /dev/null +++ b/docs/research/006-problem-landscape.md @@ -0,0 +1,263 @@ +# Pure 연구 보고서 006: 먼저 풀 문제를 고르기 + +작성일·자료 확인일: 2026-09-22 +상태: **1차 문헌 조사와 실험 전략. 새 성능 측정이나 독창성 입증은 없음.** + +## 이번 판단 + +언어·플랫폼별 실제 문제를 먼저 조사하고 Pure의 전략을 정하는 방향이 맞다. +좋은 문제는 개선의 실용적 가치를 결정하고, 실제 성능은 알고리즘·구현·작업 조건으로 검증해야 한다. +각 언어의 약점 목록에서 출발하되, 최종 연구 단위는 **작업 → 비용 발생 원인 → 기존 최선의 해결책 → 남은 비용**으로 잡는다. + +이 조사에서는 서비스·개발 도구의 운영 보고와 공식 구현 문서에서 10개 후보를 검토했다. +여러 사례에는 이미 좋은 해결책이 있다. 따라서 지금 정한 ABI·버퍼 풀·연산 규칙은 +문제에 맞춰 수정할 후보 설계다. 먼저 구현하고 그 구조가 유리한 문제를 뒤에서 고르는 순서를 피한다. + +첫 후보는 **여러 처리 단계에 걸친 버퍼 수명과 메모리 예산**이다. +사용 경험으로는 큰 파일이나 연속 데이터를 다룰 때 메모리가 예측 가능하고, +출력이 느려져도 조작·취소가 정상적으로 처리되는 것으로 나타나야 한다. +이번에는 사용자가 조작할 새 구현체가 없다. 기존 제작 화면도 이 가설을 검증하지 않는다. + +## 조사 범위와 해석 한계 + +- Discord·Instagram·VS Code 자료는 실제 시스템을 개선한 당사자의 역사적 보고다. 당시 원인이 지금도 그대로 남았다는 증거는 아니다. +- Sonic·Node.js·.NET·Rust·Java·SwiftUI·Metal·Arrow 자료는 공식 기능·구현 설명이다. 그 자체를 대규모 운영 장애나 Pure의 성능 증거로 취급하지 않는다. +- 전체 언어·플랫폼을 망라한 조사나 체계적 논문 검토가 아니다. 동적 문서의 현재 내용을 확인했지만 재현할 버전·커밋은 아직 고정하지 않았다. +- 아래의 ‘Pure 가설’과 우선순위는 자료를 바탕으로 한 우리의 추론이다. 원문 저자들이 Pure의 필요성을 주장한 것이 아니다. + +## 10개 후보와 현재 해법 + +### P01. Go: 큰 캐시의 메모리와 지연 + +Discord는 2020년 Read States 서비스에서 큰 캐시의 GC 비용과 주기적인 지연을 보고했다. +캐시를 줄이면 데이터베이스 접근이 증가하는 상충 관계가 있었다. Rust 전환에는 자료구조와 +복사 경로 등의 변경도 포함되어 언어만 바꾼 통제 실험으로 읽을 수 없다. +[Discord 운영 보고](https://discord.com/blog/why-discord-is-switching-from-go-to-rust). + +**기존 해법:** 캐시 정책·자료 배치·할당 감소·GC 설정과 메모리 제한을 먼저 검토한다. +Go의 공식 GC 안내도 CPU와 메모리 사이의 비용 관계를 설명한다. 이 안내는 Go 1.19를 기준으로 +설명한다고 명시하므로 재현 시 선택한 버전에서 다시 확인한다. +[Go GC guide](https://go.dev/doc/gc-guide). + +**Pure 가설:** 수명이 다른 데이터를 구분하면 회수 비용과 메모리 유지량을 줄일 수 있는가? +동등하게 최적화한 Go 및 C/Rust 구현이 비교 대상이다. GC가 없는 구현의 이득만 확인되면 +Pure 고유의 기여는 아직 없다. 캐시 적중률·DB 작업량도 같아야 한다. **우선순위: 보조 후보.** + +### P02. Python: 읽기 위주 데이터도 프로세스마다 복제되는 비용 + +Instagram의 pre-fork 환경에서는 참조 횟수 등 객체 관리 정보의 쓰기가 공유 페이지의 +copy-on-write를 유발했다. Meta는 immortal objects로 이 문제를 줄이는 작업을 보고했고, +PEP 683은 Python 3.12를 대상으로 채택되었다. 모든 사용자 객체가 자동으로 이 혜택을 +받는다는 의미는 아니다. +[Meta, 2023](https://engineering.fb.com/2023/08/15/developer-tools/immortal-objects-for-python-instagram-meta/), +[PEP 683](https://peps.python.org/pep-0683/). + +**Pure 가설:** 읽기 전용 본문과 변경되는 관리 정보를 분리할 가치가 남아 있는가? +기존 Python 개선과 명시적 공유 메모리가 비교 대상이다. 프로세스별 RSS를 단순 합산하지 않고 +공유·전용 페이지를 구분한다. 이 문제의 pre-fork 전제를 모바일·브라우저에 일반화하지 않는다. +**우선순위: 해당 실행 환경을 선택할 때 재검토.** + +### P03. Node.js / .NET: 느린 소비자와 버퍼 누적 + +Node.js는 streams의 역압력으로 생산 속도를 제어한다. .NET Pipelines도 풀링된 버퍼와 +writer 중단·재개 임계값을 제공하며, 소비·검사 위치를 잘못 전달하면 메모리 문제나 정체가 +생길 수 있음을 문서화한다. 역압력과 버퍼 재사용은 이미 제공되는 기능이다. +[Node.js streams](https://nodejs.org/learn/modules/backpressuring-in-streams), +[System.IO.Pipelines](https://learn.microsoft.com/en-us/dotnet/standard/io/pipelines). + +**Pure 가설:** 변환·분기·캐시·완료 대기를 합친 예산을 공유하면, 각 단계가 개별적으로 +제한을 두는 구현보다 같은 처리량·지연 조건에서 메모리를 덜 유지하는가? +올바른 역압력과 전역 예산을 추가한 기존 구현까지 비교해야 한다. +느린 소비자에서 입력을 무조건 버려 얻은 수치는 동일 기능의 성능 개선이 아니다. +**우선순위: 첫 연구, P04와 함께 검토.** + +### P04. ByteDance Sonic: 복사를 생략한 결과 원본 전체가 살아남는 비용 + +Sonic은 디코딩한 문자열이 원본 JSON을 참조하면서, 작은 결과를 보관해도 원본 버퍼가 +유지될 수 있음을 설명한다. `CopyString` 및 검색 결과의 `CopyReturn` 같은 선택지도 제공한다. +복사량과 장기 메모리 유지량 사이에 상충 관계가 있다는 구체적인 사례다. +[Sonic 공식 저장소](https://github.com/bytedance/sonic#tips). + +**Pure 가설:** 원본 크기·남기는 범위·메모리 압력을 이용해 공유와 복사를 선택하면 +일괄 정책보다 유리한가? 항상 복사, 항상 공유, 단순 크기 임계값, 기존 라이브러리 설정이 +비교 대상이다. 미래의 실제 보관 시간을 미리 아는 정책을 실용 구현처럼 비교하지 않는다. +**우선순위: 첫 연구. 기존 옵션보다 남는 이득이 있는지부터 확인.** + +### P05. Rust: 특수화의 실행 성능과 빌드 비용 + +rustc는 제네릭의 구체 타입별 코드를 생성하는 monomorphization을 사용한다. +공식 가이드는 실행 성능의 이점과 컴파일 시간·바이너리 크기 비용을 함께 설명한다. +이것만으로 Rust 컴파일러 전체의 크기나 어떤 앱의 메모리 사용 원인을 설명할 수는 없다. +[Rust Compiler Development Guide](https://rustc-dev-guide.rust-lang.org/backend/monomorph.html). + +**Pure 가설:** 공통 실행과 제한적 특수화를 조합해 원하는 실행 성능에서 빌드 비용을 +통제할 수 있는가? 컴파일러 배포 크기, 빌드 시간·최대 RAM, 산출물 크기, 실행 RAM을 +별도로 기록한다. 동등한 기능을 가진 비교가 필요하며 현재 작은 DSL의 크기를 범용 언어와 +직접 비교하지 않는다. **우선순위: 모든 단계의 관리 지표. 첫 대표 문제로는 보류.** + +### P06. JVM: 이미 해결된 제약을 연구 대상으로 고르는 위험 + +JDK 24는 `synchronized` 안에서 대기하는 virtual thread가 carrier thread를 붙잡는 +제약을 완화하는 JEP 491을 반영했다. 이전 버전의 대표 문제를 최신 Java 전체의 +한계로 삼으면 출발점부터 비교가 틀어진다. 모든 native/FFI 상황의 제약이 없어졌다는 뜻은 아니다. +[Oracle JDK 24 변경 사항](https://docs.oracle.com/en/java/javase/24/migrate/significant-changes-jdk-24.html). + +**Pure 판단:** 이 과거 제약은 첫 연구 후보에서 제외한다. 실제 작업의 남은 대기 비용이 +확인되면 지원 중인 대상 JDK와 비교한다. 작은 스케줄러를 만드는 것만으로 우월성을 주장하지 않는다. + +### P07. TypeScript / VS Code: 작은 변경에 큰 문서를 다시 처리하는 비용 + +VS Code의 2021년 괄호 색상 개선은 확장 API의 제약을 넘어서 토큰 정보와 증분 자료구조를 +활용했다. 보고된 큰 개선은 특정 괄호 갱신 작업에 대한 것으로, 앱 전체나 언어 간 속도 비율이 아니다. +기존 확장 호스트의 격리는 무거운 작업 중 UI 반응성을 지키는 역할도 했다. +[VS Code 개발팀 보고](https://code.visualstudio.com/blogs/2021/09/29/bracket-pair-colorization). + +**Pure 가설:** 작은 수정이 유발하는 재계산·데이터 이동 범위를 예산 안에서 제한할 수 있는가? +비교 대상도 증분 알고리즘을 사용해야 한다. 의존성 추적 메모리와 초기 구축·대규모 수정 비용을 +포함한다. **우선순위: 두 번째 연구 후보, 실제 문서·데이터 편집 기능과 연결.** + +### P08. SwiftUI: 상태 의존성과 갱신 범위 + +Apple은 SwiftUI 성능 분석에서 의존성, 뷰의 identity, 비용이 큰 갱신 작업을 다룬다. +SwiftUI 자체가 의존성을 추적하므로 변경된 부분만 처리한다는 발상만으로 차별화되지는 않는다. +[Apple WWDC23: Demystify SwiftUI performance](https://developer.apple.com/videos/play/wwdc2023/10160/). + +**Pure 가설:** 상태·입력·출력의 연결 정보로 불필요한 갱신을 줄이면서 추적 메모리도 +제한할 수 있는가? 적절히 상태를 분리한 기존 구현과 비교한다. 렌더러 차이와 상태 처리 차이를 +분리하고 동일한 화면·상호작용을 유지한다. **우선순위: P07과 묶어 후속 검토.** + +### P09. Apple Metal: 통합 메모리에서도 남는 자원 수명과 동기화 + +Apple GPU는 시스템 메모리를 공유하지만 자원 접근 방식에 따라 shared/private/memoryless를 +선택한다. shared 자원도 CPU·GPU 접근 순서를 동기화해야 한다. +[Apple 저장 모드 안내](https://developer.apple.com/documentation/metal/choosing-a-resource-storage-mode-for-apple-gpus), +[shared 접근 계약](https://developer.apple.com/documentation/metal/mtlstoragemode/shared). + +**Pure 가설:** 입력에서 GPU 완료까지 자원 수명을 연결해 임시 자원과 대기를 줄일 수 있는가? +잘 구성한 Metal 직접 구현이 비교 대상이다. 동시에 진행하는 프레임 수와 출력 품질을 맞추고 +CPU·GPU 대기 및 메모리를 함께 측정한다. 현재 Linux 측정은 사용자의 Apple Silicon에서 +실행한 증거가 아니다. **우선순위: 실제 Mac 프로파일을 확보한 뒤.** + +### P10. C/C++ 및 다중 언어 경계: 이미 있는 무복사 ABI + +Apache Arrow C Data Interface는 같은 프로세스 안에서 언어·런타임 간 columnar 데이터를 +공유할 수 있는 작은 C 인터페이스와 해제 계약을 제공한다. 프로세스 간 전송이나 영속 저장 형식은 +그 인터페이스의 범위가 아니다. +[Arrow C Data Interface](https://arrow.apache.org/docs/format/CDataInterface.html). + +**Pure 가설:** 기존 인터페이스로 표현되지 않는 비동기 수명·예산 관리가 필요한가? +Arrow에 맞는 데이터는 이를 활용하는 방안을 먼저 비교한다. 자체 ABI의 필요성은 함수 호출, +버전 처리, 소유권, 호환 비용까지 보여야 한다. **우선순위: 재사용·연동 기준.** + +## 첫 연구 질문 + +> 여러 단계가 입력을 나눠 읽고 일부 결과를 오래 보관하는 작업에서, +> 전체 예산과 원본 버퍼 수명을 함께 관리하면 기존의 잘 구성한 구현보다 +> 같은 처리량·지연 조건에서 메모리 유지량을 줄일 수 있는가? + +**귀무가설 H0:** 기존의 제한된 큐, 메모리 풀, 단순 복사 정책을 조합하면 Pure 후보와 +동등하거나 더 좋은 비용을 얻는다. 이 경우 공통 라이브러리나 어댑터 수준으로 해결하고 +그 문제를 이유로 새 언어·ABI·스케줄러를 확대하지 않는다. + +**대안 가설 H1:** 단계 간 예산과 수명 정보를 공유하는 정책이, 입력 분포가 달라져도 +단순 정책 대비 유리한 메모리·처리량·지연 조합을 만든다. 그 이득이 관리 정보·계측·호스트 +비용을 포함해 남고 다른 작업에도 적용되는지 확인한다. 이 가설은 아직 검증되지 않았다. + +현재 초안의 버퍼 전체 단위 대여와 완료 회수 시점도 수정 대상이다. +큰 원본의 작은 구간 때문에 전체 할당이 유지되거나 완료 회수가 밀리면 오히려 비용이 커질 수 있다. +복사를 줄이는 지표 하나만 목표로 삼지 않는다. + +## 재현할 작업과 비교 순서 + +첫 작업 후보는 **큰 로그·구조화 데이터에서 필드를 추출하고, 일부 결과를 보관하면서 +느린 출력으로 내보내는 CLI**다. 이는 연구용 작업 제안이며 제품 범위를 확정한 것은 아니다. +후속으로 압축 입력과 문서 탐색 화면을 연결할 수 있지만 첫 비교에 모두 넣지 않는다. + +1. **기존 구현에서 비용 재현.** 단일 프로세스의 제한된 처리 흐름으로 시작한다. + 입력 크기, 레코드 크기 분포, 보관 비율, 소비자 속도, 분기 수를 독립적으로 바꾼다. + 합성 데이터는 생성 규칙·seed를 공개하고 실제 운영 추적이라고 부르지 않는다. +2. **비교 구현 개선.** 항상 복사·공유·단순 임계값 정책을 비교하고, + 올바른 역압력·전역 예산·버퍼 풀까지 적용한다. 튜닝에 쓴 입력과 평가 입력을 분리한다. +3. **남은 비용 분석.** 원본 유지·대기열·파싱·할당·I/O 중 비용이 어디에 있는지 확인한다. + Pure가 건드릴 수 있는 경로의 비중이 작으면 우선순위를 낮춘다. +4. **최소한의 Pure 정책 비교.** 같은 언어·파서·I/O 경로에서 정책만 바꿔 원인을 분리한다. + 외부 언어·플랫폼 비교는 별도 실험으로 보고한다. 파서 교체로 얻은 이득을 수명 정책의 이득으로 돌리지 않는다. +5. **실제 제작 경험.** 가치가 확인된 작업을 사용자가 빈 작업물에서 작성·수정하게 한다. + 새 필터·출력·보관 정책을 추가할 때마다 실행기 코드를 고쳐야 하는지 기록한다. + +큰 파일 하나만으로 대규모 서비스의 특성을 대체할 수 없다. 데이터/메모리 비율, +동시 작업 수, 보관 시간 분포, 부하 급증, 느린 소비자의 분포를 드러내야 한다. +네트워크를 붙일 때는 도착 부하와 큐 대기 시간을 기록하고, 느려진 만큼 요청 발생도 늦어져 +지연이 작아 보이는 측정 오류를 피한다. 분산 저장소·핫키·질의 설계의 비용은 별도로 분석한다. + +## 측정과 탈락 기준 + +| 축 | 반드시 포함할 것 | +|---|---| +| 정확성 | 출력 내용·순서, 잘린 입력, 실패·취소, 완료 후 자원 반환 | +| 메모리 | 예약·사용·유지 바이트, 큐·풀·관리 정보, 라이브러리 내부 할당, 프로세스 메모리 | +| 수명 | 원본 할당별 마지막 참조 시점, 유지 바이트의 시간 적분(byte·seconds), 최고 유지량 | +| 복사 | 경로별 복사 바이트, 공유로 연장된 수명, 계측 밖 경로 | +| 처리 | 완료한 유효 작업량, 처리량, 큐 대기를 포함한 지연 분포, 거절·손실·취소 수 | +| 구축 비용 | 도구 버전, 빌드 시간·최대 RAM, 실행 파일·모듈·의존성 크기 | +| 측정 조건 | OS·CPU·RAM, 빌드 옵션·커밋, 입력·설정, 워밍업, 반복 분산, 계측 자체 비용 | + +공유 원본은 할당별로 한 번만 센다. 풀 예약량·RSS·GPU 통계를 무조건 더하지 않는다. +관리하는 버퍼 예산과 전체 프로세스 메모리 상한을 구분한다. 취소가 접수됐다는 이유로 +아직 접근 중인 메모리를 반납해 성능을 높이는 구현은 실패다. + +실험 전에 작업의 메모리 한도와 필요한 지연·처리량을 정한다. 숫자는 실제 사용 조건에서 정하고 +유리한 결과를 본 뒤 고르지 않는다. 같은 조건에서 반복 측정의 변동보다 확실한 차이가 있는지, +다른 입력에서는 어떤 손해가 생기는지 함께 보고한다. + +다음 중 하나면 가설을 기각하거나 범위를 줄인다. + +- 단순한 기존 정책을 튜닝하면 차이가 사라진다. +- 관리 정보·라이브러리·호스트 비용을 포함하면 이득이 사라진다. +- 메모리를 줄인 대가로 필요한 처리량·지연·정확성을 만족하지 못한다. +- 특정 입력에만 유리하며 평가에 쓰지 않은 입력에서 재현되지 않는다. +- 새 런타임 없이 작은 라이브러리 변경으로 같은 결과를 얻는다. 이 경우 그 변경 자체의 가치를 평가한다. + +## 연구 기여를 판단하기 위한 다음 문헌 단계 + +운영 사례는 문제의 존재를 보여주지만 새로운 해법의 독창성을 증명하지 않는다. +첫 문제의 잔여 비용이 확인되면 다음 주제의 원 논문·공개 구현과 비교표를 만든다. + +- 영역 기반 메모리 관리와 소유권/대여: 회수 단위와 수명 표현의 기존 해법. +- 스트리밍 실행기의 admission control·역압력·메모리 예약: 전역 예산의 기존 해법. +- 공유 버퍼의 보관·복사 정책: 미래 수명을 모르는 온라인 결정과 관리 비용. +- 증분 계산·질의 처리: 입력 변화에 비례한 작업량과 추적 상태 비용. + +각 연구의 전제, 보장, 비용, 실패 조건, 재현 가능 구현을 기록한다. +현재는 이 논문 검토를 완료하지 않았으므로 새로운 원리나 세계 최초라는 표현을 사용하지 않는다. + +## 기존 계획에 대한 결정 + +[005의 계약 초안](005-foundation-contract.md)은 유지하되, 구현 우선순위 앞에 +**기존 해법의 재현과 남은 비용 확인**을 둔다. 버퍼 풀·자체 ABI·커널 우회 중 무엇이 필요한지는 +그 결과에서 결정한다. 컴파일러 크기 관리는 병행하는 측정 항목이다. + +다음 산출물은 새 문법 데모가 아니라 첫 후보의 **재현 가능한 비교 구현과 비용 보고서**다. +첫 후보에서 차별화가 확인되지 않으면 그 결과를 남기고 P07/P08 또는 실제 Mac 작업을 검토한다. +압축·암호화·문서·미디어·OS의 장기 목표는 유지하지만, 한 실험의 결과를 모든 분야의 성능으로 확대하지 않는다. + +2026-09-22 진행 기록: [007의 첫 비교 실험](007-buffer-retention-results.md)에서 단일 FIFO의 +원본 유지 비용을 재현했다. 기존 복사·선별 읽기 정책으로 개선됐으며 Pure 고유의 이점은 확인하지 못했다. +007의 범위에는 여러 소비자와 실제 파서의 잔여 비용 검증이 포함되지 않았다. + +추가 진행: [008](008-json-fanout-results.md)에서 실제 JSON 파서와 다중 소비자의 제한된 작업을 +검증했다. 기존 정책 대비 고유한 이점을 확인하지 못해 P03/P04는 기반 품질 과제로 유지하고, +다음 대표 연구 후보는 P07/P08의 증분 편집·갱신 비용으로 옮긴다. 실제 운영 작업 전체에 대한 결론은 아니다. + +작업 범위 보완: 공유·복사 정책 비교는 여기서 종료하고 원자료를 보존한다. +P07/P08는 아직 구현 대상으로 확정하지 않는다. 먼저 기존 문헌·공개 구현에서 +구체적인 편집 작업의 비용과 기존 해법의 한계를 확인한다. **기존 해법 채택으로 +충분한지, Pure의 실행 방식 변경이 필요한지**를 가르는 근거만 조사한다. +작은 변경을 전체 재계산과 비교해 빠르다는 사실을 재현하는 데 그치면 탈락이다. +남은 비용과 최소 확인 방법을 특정할 수 있을 때만 비교 구현으로 넘어간다. +세부 작업 기준은 [협업 방식](000-working-method.md)을 따른다. + +자체 구현 진행: P07/P08의 일반적인 증분 처리 벤치마크 대신, 초기 연구 질문 중 +추적 기록의 제거를 [작은 실행 코어](../../prototypes/forgetful-core/README.md)로 +구체화했다. 변경 기록이 부족할 때의 재계산, 조건부 의존성의 보존, 작업 한도 내 +거절을 확인했다. 이는 자체 실행 계약 구현이며 신규성·성능 우위의 결론은 아니다. diff --git a/docs/research/007-buffer-retention-results.md b/docs/research/007-buffer-retention-results.md new file mode 100644 index 0000000..36fb684 --- /dev/null +++ b/docs/research/007-buffer-retention-results.md @@ -0,0 +1,195 @@ +# Pure 연구 보고서 007: 버퍼를 공유할 때와 복사할 때 + +작성일: 2026-09-22 +상태: **비교 CLI 구현·정확성 검사·Linux AArch64 측정 완료. Pure의 성능 우월성은 확인하지 못함.** + +## 이번에 알아낸 것 + +첫 후보의 문제는 재현됐다. 작은 결과 때문에 큰 원본 버퍼가 오래 유지되면 메모리가 커진다. +하지만 이번 범위에서는 **일반적인 크기 기준 복사와 필드 선별 읽기만으로 큰 개선이 가능했다.** +현재 메모리 압력에 따라 복사를 결정하는 후보 정책은 그 단순한 방법보다 뚜렷하게 낫지 않았다. +이 결과를 근거로 새로운 런타임·ABI·컴파일러를 확대할 이유는 아직 없다. + +같은 64개 큐 한도에서 64 KiB 레코드의 64바이트만 남기는 작업을 비교했다. +공유 방식의 최대 관리 메모리는 약 **4.070 MiB**, 일반 복사 방식은 **0.137 MiB**였다. +이는 실험 코드가 요청한 저장 공간의 약 30배 차이다. 전체 앱 RAM의 30배 개선이 아니다. +해당 조건의 Linux RSS 근삿값은 각각 약 5.05 MiB와 1.56 MiB였다. + +사용 경험으로 이어질 수 있는 차이는 큰 데이터를 처리할 때 적은 메모리로 작업을 지속하는 것이다. +다만 메모리가 줄면 큐가 더 길어져 응답이 늦어질 수도 있었다. 버퍼 용량과 지연을 함께 제한해야 한다. +이번 근거는 합성 입력을 사용한 실제 네이티브 실행 측정이다. 실제 서비스·Mac·Pure 제작 경험의 측정은 아니다. + +사용자는 이번에 새 데모를 평가할 필요가 없다. 실행 가능한 산출물은 +[비교 CLI와 재현 방법](../../research/experiments/002-buffer-retention/README.md)이며, +현재 제작 화면의 문법과 동작은 바꾸지 않았다. + +## 구현한 비교 + +같은 C17 프로그램 안에서 입력 처리·큐·출력·메모리 예산·계측을 공유하고 정책을 바꾼다. +Pure VM을 호출하지 않으며 새로운 메모리 관리 원리를 구현했다고 주장하지 않는다. + +| 방식 | 처리 | +|---|---| +| `borrow` | 선택한 부분이 필요할 동안 원본 전체를 보관 | +| `copy` | 선택한 부분을 별도 버퍼에 복사한 뒤 원본 반납 | +| `threshold2/4/8` | 원본/선택 버퍼의 할당 용량 비율이 2·4·8 이상이면 복사 | +| `pressure` | 현재 활성 버퍼가 가용 예산의 절반을 넘으면 더 작은 결과를 복사 | +| `project` | 입력에서 필요한 부분만 결과 버퍼로 읽고 나머지는 작은 임시 공간으로 읽어서 버림 | +| `copy_no_pool` | 일반 복사와 같은 정책·용량 구분을 쓰되 버퍼 재사용 캐시를 끔 | + +모든 방식에 전체 예산과 큐 개수 제한을 적용했다. 캐시의 빈 버퍼도 예산에 넣고 필요하면 반납한다. +복사 중 원본과 결과가 동시에 존재하는 공간도 포함했다. 원본 크기만큼 필요한 공간이 없으면 +대기 중 출력을 처리하고, 그것으로도 불가능하면 명시적으로 실패한다. + +입력은 길이·선택 범위가 헤더에 담긴 실험용 바이너리 레코드다. 모든 입력 바이트를 실제로 읽는다. +JSON 파서나 압축 라이브러리를 비교한 것이 아니다. `project`의 범위는 이처럼 필요한 위치를 +알 수 있는 입력이다. 범용 문서에서 같은 효과를 얻으려면 별도 검증이 필요하다. + +## 실행 조건과 검증 + +- 호스트: Raspberry Pi 계열 Linux AArch64, Cortex-A76, GCC 14.2.0. 사용자 Apple Silicon에서 실행하지 않았다. +- 빌드: C17, `-O2`, 경고를 오류로 처리, 진단용 assert·계측 활성화. 최적화 빌드와 ASan/UBSan 빌드를 따로 검사했다. +- 입력: 조건별 2,048개 레코드, 약 128~169 MiB. seed 20260922, 크기·선택 범위·바이트 생성 규칙과 해시 보관. +- 일반 조건: 관리 공간 8 MiB, 큐 최대 512개. 좁은 예산 조건은 2 MiB. 별도 민감도 검사는 큐를 64개로 제한했다. +- 느린 소비자: 각 결과는 enqueue 뒤 최소 5 ms 보관, 연속 출력 완료 사이 최소 20 μs 간격. + 실제 sleep과 파일 I/O를 수행하지만 이 지연은 합성 조건이다. OS 기상 지연 때문에 정확히 20 μs마다 출력되는 것은 아니다. +- 6개 조건 × 8개 방식 × 5회 = **240개 측정**. 방식별 워밍업 후 각 반복에서 순서를 섞었고 모든 출력 해시가 일치했다. +- 일반 빌드와 ASan/UBSan 합계 **88개 검사 그룹** 통과. 바이트 단위 출력 비교, 잘린 입력, + 잘못된 범위, 예산 부족, 완료 출력의 순서, 취소, SIGTERM, 실제 출력 실패(`/dev/full`), 버퍼 회수를 검사했다. + +입력 생성·빌드·결과 검사는 시간 측정 밖이다. 측정에는 읽기·쓰기·복사·대기·계측·버퍼 정리가 포함된다. +파일 캐시를 사용하는 실행이며 출력에 `fsync`를 하지 않는다. 한 CPU로 고정했으나 온도·전력·다른 작업은 통제하지 않았다. + +## 결과 1: 같은 큐 한도에서도 기존 복사 정책이 효과적 + +64 KiB 레코드에서 64바이트를 선택하고, 모든 방식의 큐를 64개로 제한했다. +아래는 5회 중앙값이다. 메모리는 고정 관리 공간·임시 공간·버퍼·빈 캐시를 포함한 요청 바이트다. +시간은 전체 2,048개 처리가 끝날 때까지의 값이며 합성 대기가 포함된다. + +| 방식 | 최대 관리 공간 MiB | RSS 근사 MiB | 완료 시간 ms | 프로세스 내부 P99 상한 ms | +|---|---:|---:|---:|---:| +| 원본 공유 | 4.070 | 5.05 | 167.22 | 7.34 | +| 항상 복사 | 0.137 | 1.56 | 168.36 | 7.86 | +| 크기 비율 2 기준 | 0.137 | 1.56 | 167.15 | 7.86 | +| 메모리 압력 기준 | 4.070 | 5.50 | 167.12 | 7.34 | +| 필요한 부분만 읽기 | 0.074 | 1.52 | 167.51 | 7.86 | + +공유의 완료 시간 범위는 167.16~169.33 ms, 복사는 166.68~177.07 ms였다. +이 표로 작은 시간 차이의 우열이나 동일한 서비스 SLO 충족을 주장하지 않는다. +여기서 분명한 관찰은 **작은 결과를 독립시키면 유지해야 하는 원본 공간이 줄어든다**는 것이다. + +항상 복사해도 전체 명시적 복사는 128 KiB였다. 원본 입력 약 128 MiB 전체를 복사한 것이 아니다. +형식이 허용한 선별 읽기는 그 복사도 없앨 수 있었다. 다만 read/write 등 다른 데이터 이동은 여전히 있다. + +크기 관계로도 설명할 수 있다. 대략 Q개 결과가 남아 있을 때 원본 공유는 `Q × 원본 용량`, +복사는 `Q × 선택 용량 + 처리 중인 원본 용량`을 요구한다. 실제 측정에는 관리 공간·캐시·할당 반올림이 더해진다. +이 비용 관계를 Pure의 새 발견으로 취급하지 않는다. + +## 결과 2: 메모리만 줄이면 큐 대기는 커질 수 있다 + +큐 한도를 512개로 둔 같은 작은 선택 조건에서, 공유는 예산 때문에 최대 126개를 보관했고 +복사는 512개까지 보관했다. 최대 관리 공간은 공유 7.963 MiB에서 복사 0.188 MiB로 줄었지만, +프로세스 내부 P99 상한 중앙값은 약 9.44 ms에서 41.94 ms로 커졌다. + +그만큼 작업이 큐 안에서 기다렸다. 입력을 아직 읽지 않아 밖에서 대기한 시간은 이 지연에 포함되지 않는다. +따라서 공유 방식의 서비스 전체 지연이 더 짧다고 해석할 수도 없다. +**메모리 예산·큐 길이·외부 도착률을 함께 정해야 지연에 관한 비교가 가능하다.** + +이 관찰 후 추가한 것이 앞의 64개 큐 검사다. 사후에 추가한 탐색 실험임을 +[v2 계획](../../research/experiments/002-buffer-retention/plan.json)에 명시했다. + +## 결과 3: 복사를 많이 하면 항상 좋은 것도 아니다 + +64 KiB 중 48 KiB를 남기는 조건에서는 이번 할당기의 용량 반올림 때문에 선택 버퍼도 64 KiB가 된다. +공유와 복사 모두 최대 관리 공간이 약 7.963 MiB였고, 항상 복사 방식은 추가로 96 MiB를 복사했다. +크기 기준 정책은 이 불필요한 복사를 하지 않았다. + +즉, 원본과 선택 데이터의 관계뿐 아니라 **실제 할당 용량**을 고려해야 한다. +다른 용량 구분·정확한 크기 할당기에서는 결과가 달라질 수 있다. 이번 작은 풀을 최선의 할당기라고 주장하지 않는다. + +메모리 부족이 없는 빠른 소비자 조건에서는 대부분의 방식이 약 0.149 MiB만 관리했고, +전체 완료 시간 중앙값도 약 20~23 ms 범위였다. 느린 소비자에서 관찰한 큰 유지량 차이를 +모든 실행에 일반화할 수 없다. + +## 결과 4: 좁은 예산에서는 원본 유지가 진행 자체를 늦출 수 있다 + +4~256 KiB 원본과 다양한 선택 비율이 섞인 입력을 2 MiB 예산으로 처리했다. +모든 방식이 거의 같은 최대 관리 공간까지 사용한다. 이 경우에는 완료 시간과 복사량이 달라졌다. + +| 방식 | 완료 시간 중앙값 ms | 측정 범위 ms | 명시적 복사 MiB | +|---|---:|---:|---:| +| 원본 공유 | 493.63 | 491.78~495.15 | 0 | +| 항상 복사 | 191.25 | 191.04~191.77 | 51.24 | +| 크기 비율 2 기준 | 188.82 | 188.31~201.97 | 27.16 | +| 크기 비율 4 기준 | 225.45 | 224.73~229.05 | 10.71 | +| 크기 비율 8 기준 | 283.68 | 282.26~289.85 | 3.65 | +| 메모리 압력 기준 | 190.69 | 189.79~193.71 | 27.07 | +| 필요한 부분만 읽기 | 175.31 | 174.67~179.28 | 0 | + +명시적 복사 바이트가 적다고 완료가 빠르지는 않았다. 원본을 오래 보관하면 다음 입력을 받아들일 +공간이 부족해지고, 합성 보관 시간을 기다리는 동안 작업 진행이 제한된다. +압력 기준 정책은 단순한 크기 비율 2 정책과 비슷한 결과였다. 후보 정책의 고유한 이점은 확인하지 못했다. + +이 결과는 해당 로컬 파이프라인의 처리 시간이다. 약 2.6배의 시간 차이를 +서버·파서·Pure 전체의 성능 개선으로 표기하지 않는다. + +버퍼 재사용의 효과도 별도로 확인했다. 64개 큐의 작은 선택 조건에서 복사 방식의 실제 할당 호출은 +캐시를 끄면 4,096회, 켜면 65회였지만 완료 시간의 뚜렷한 개선은 확인하지 못했다. +할당 횟수 감소와 사용자 체감 성능 개선은 서로 다른 주장이다. + +## 계측 중 발견하고 수정한 문제 + +첫 실행에서 모든 RSS가 약 27.42 MiB로 동일했다. 이를 결과로 확정하지 않고 조사했다. +Linux `getrusage`의 자원 통계는 exec를 지나서도 보존되므로, 이 실행 경로의 값에는 +프로그램 실행 전 이력이 섞여 있었다. +[getrusage 문서](https://man7.org/linux/man-pages/man2/getrusage.2.html). + +현재 결과는 Linux `/proc/self/status`의 현재 메모리 이미지 `VmHWM`을 별도로 읽고, +원래 `rusage`도 구분하여 남긴다. Linux 문서는 이 RSS 계측도 근삿값이라고 설명한다. +정확한 요청 바이트 계수와 동일한 지표가 아니다. +[proc status 문서](https://man7.org/linux/man-pages/man5/proc_pid_status.5.html). + +[첫 실행 기록](../../research/experiments/002-buffer-retention/results/2026-09-22-linux-aarch64/audit.json)과 +당시 소스는 보존했다. 본문의 수치는 모두 수정된 v2 실행에서 가져왔다. +수정 후 큰 원본 유지와 작은 선택 결과가 RSS에서도 구분되는지 회귀 검사를 추가했다. + +## 이 실험이 아직 다루지 않은 것 + +- 하나의 원본을 여러 소비자가 공유하는 분기, 영속 캐시, 독립적인 여러 처리 단계의 예산 조정. +- Sonic·Node.js·.NET·Arrow 자체의 현재 구현과 직접 비교. +- 일반 JSON·문서·압축 파서와 외부 라이브러리 내부 메모리. +- 독립적인 요청 도착, 서비스 전체 지연, 네트워크·GPU·비동기 취소 경합. +- 온도·전력 통제, 별도 계측 오버헤드 보정, Mac 실행, 통계적 유의성 검정. +- 사용자에게 필요한 실제 작업의 메모리·지연 목표와 새 도구의 개발 경험. + +따라서 006의 여러 단계에 걸친 가설 전체를 기각한 것은 아니다. +**단일 입력·단일 소비자에서의 공유/복사 선택만으로는 Pure의 필요성을 입증하지 못했다**는 결론이다. + +## 다음 설계 결정 + +1. 무복사나 전용 할당기 자체를 대표 차별점으로 삼지 않는다. 기존 크기 기준 정책을 필수 비교 대상으로 유지한다. +2. [버퍼 수명 초안](../design/002-memory-lifetime.md)에는 필요한 계측 질문이 생겼다. + 원본 할당과 선택 범위, 마지막 소비자, 빈 풀의 유지량, 큐 밖의 대기까지 구분해야 한다. + 이 실험만으로 현재 계약의 비동기 보장이 구현·검증된 것은 아니다. +3. 다음 후보 검증은 실제 파서와 여러 소비자가 같은 원본을 공유하는 작업으로 한정한다. + 그 작업의 기존 구현에서도 남는 비용이 확인될 때 전역 수명·예산 정책과 선행연구 비교를 진행한다. + 같은 해법으로 충분하면 이 주제의 차별화 우선순위를 낮추고 증분 편집 후보로 이동한다. + +## 재현·원자료 + +- [비교 코드](../../research/experiments/002-buffer-retention/benchmark.c), [실행·검증 도구](../../research/experiments/002-buffer-retention/run.py) +- [실행 환경·빌드·소스 해시](../../research/experiments/002-buffer-retention/results/2026-09-22-linux-aarch64-v2/environment.json) +- [입력·출력 해시](../../research/experiments/002-buffer-retention/results/2026-09-22-linux-aarch64-v2/fixtures.json) +- [검증 기록](../../research/experiments/002-buffer-retention/results/2026-09-22-linux-aarch64-v2/verification.json) +- [240개 원자료](../../research/experiments/002-buffer-retention/results/2026-09-22-linux-aarch64-v2/raw.csv), [중앙값·범위](../../research/experiments/002-buffer-retention/results/2026-09-22-linux-aarch64-v2/summary.json) + +```sh +python3 research/experiments/002-buffer-retention/run.py --check-only +python3 research/experiments/002-buffer-retention/run.py --output /tmp/pure-retention-results +``` + +출력 경로는 새 디렉터리여야 한다. 측정 도구는 기존 결과를 덮어쓰지 않는다. + +2026-09-22 후속: [008](008-json-fanout-results.md)에서 실제 JSON 파서와 최대 세 소비자로 +확장했다. 그 범위에서도 기존 복사·복사본 공유 정책이 효과적이었으며, 대표 연구 문제로서의 +우선순위를 낮추고 증분 편집·상태 갱신 후보로 이동하기로 했다. diff --git a/docs/research/008-json-fanout-results.md b/docs/research/008-json-fanout-results.md new file mode 100644 index 0000000..5fcc5d0 --- /dev/null +++ b/docs/research/008-json-fanout-results.md @@ -0,0 +1,202 @@ +# Pure 연구 보고서 008: 실제 JSON 파서와 여러 소비자의 버퍼 수명 + +작성일: 2026-09-22 +상태: **실제 파서를 연결한 비교 구현·검증·210회 측정 완료. Pure 고유의 우위는 확인하지 못함.** + +## 이번 판단 + +실제 JSON과 여러 소비자를 붙여도 원본 유지 비용은 재현됐다. +그리고 이번 범위에서도 **작은 결과를 일찍 복사하고 같은 결과의 복사본을 공유하는 기존 방법이 효과적이었다.** +소비자가 하나만 남았을 때 뒤늦게 복사하는 후보는 원본의 유지 시간을 줄였지만, +최대 메모리를 충분히 줄이지 못하거나 단순 크기 기준보다 진행이 느렸다. + +이제 ‘공유 버퍼를 어떻게 복사할 것인가’만으로 새로운 언어·실행기·ABI의 필요성을 설명하지 않는다. +수명·예산 관리는 Pure의 기본 품질 요건으로 남기고, 연구의 대표 문제로서 우선순위를 낮춘다. +다음 우선 후보는 006에서 정한 **작은 변경이 큰 재계산을 일으키는 편집·상태 갱신 문제**다. + +사용 경험에 연결되는 관찰은 분명하다. 작은 메타데이터를 느린 소비자에게 넘겼다는 이유로 +이미 처리한 큰 본문까지 붙잡을 필요는 없다. 다만 이것을 사용자 앱이나 Pure 문법으로 구현한 것은 아니다. +이번 산출물은 [실행 가능한 비교 CLI](../../research/experiments/003-json-fanout/README.md)이며, +사용자가 새 데모를 조작해 평가할 과제는 없다. + +## 이전 실험에서 보완한 것 + +[007](007-buffer-retention-results.md)은 미리 알려진 바이트 범위와 단일 소비자만 다뤘다. +이번에는 다음을 추가했다. + +- **실제 JSON 파싱:** 버전을 고정한 yyjson 0.12.0으로 줄마다 JSON 전체를 파싱한다. + 선택하지 않는 필드도 구문 검사를 거친다. Python의 별도 JSON 디코더와 실제 출력 바이트를 비교한다. +- **최대 세 소비자:** 서로 다른 필드 또는 같은 필드를 소비하고, 각각 다른 시점에 완료한다. + 소비자별 순서는 유지하지만 소비자 사이의 출력 순서는 달라질 수 있다. +- **공유 소유권:** 같은 원본을 여러 번 세지 않는다. 마지막 참조가 반납되어야 해당 할당을 반환한다. +- **파서 내부 메모리:** yyjson의 할당·재할당·해제 콜백을 같은 예산에 연결했다. + 입력·결과만 작게 보이게 하고 파서 작업 공간을 제외하는 비교를 피했다. + +yyjson은 입력을 직접 사용해 문자열을 디코딩하는 in-situ 옵션을 제공한다. +필요한 문자열의 위치와 길이를 얻은 뒤 파서 문서 메타데이터는 즉시 해제하고, +문자열이 있는 입력 버퍼의 소유권만 유지했다. +실험에 사용한 [고정 버전 소스](https://github.com/ibireme/yyjson/tree/8b4a38dc994a110abaec8a400615567bd996105f)와 +MIT 라이선스·해시는 저장소에 함께 보관했다. + +## 비교 조건 + +모든 방식이 같은 파서, 읽기 경로, 소비자 스케줄러, 예산 검사와 출력 형식을 사용한다. +각 소비자의 큐는 최대 64개다. 기본 지연은 첫 소비자 0 ms, 둘째 5 ms, 셋째 8 ms다. +이는 실제 sleep으로 넣은 **합성 보관 시간**이며 네트워크나 서비스의 관측 지연이 아니다. + +| 방식 | 결정 | +|---|---| +| `borrow` | 모든 소비자가 원본을 참조 | +| `copy` | 소비자마다 필요한 문자열을 별도로 복사 | +| `shared_copy` | 같은 원본 범위를 선택한 소비자끼리 복사본 하나를 공유 | +| `threshold2/8` | 원본/결과의 할당 용량 비율이 2·8 이상이면 복사하고 같은 복사본을 공유 | +| `compact` | 소비자 완료 후 원본 참조가 하나만 남으면 작은 결과를 복사 시도 | +| `copy_no_pool` | 독립 복사와 같은 용량 구분을 쓰되 빈 버퍼 캐시는 끔 | + +`compact`는 현재 참조 상태만 보는 단순 후보다. 미래의 보관 시간을 미리 알지 않는다. +다른 소비자가 여전히 원본을 참조할 때 그 버퍼를 옮기지 않는다. +단일 소비자 조건에서는 완료 전 참조 수 변화가 없으므로 이 후보의 복사 시도도 없다. +이 특정 후보가 모든 동적 정책을 대표하지는 않는다. + +6개 조건 × 7개 방식 × 5회로 **210개 측정**을 수행했다. +기본 입력은 본문 32 KiB·패딩 8 KiB와 작은 문자열·메타데이터를 포함한 JSON 1,024개, 약 40.13 MiB다. +혼합 입력은 약 47.57 MiB, 메타데이터가 많은 입력은 512개·약 5.02 MiB다. +모두 생성 규칙과 seed를 공개한 합성 데이터다. 실제 운영 트래픽은 아니다. + +호스트는 Linux AArch64, Cortex-A76, GCC 14.2.0이다. 한 CPU로 고정했으나 온도·전력은 통제하지 않았다. +워밍업 뒤 방식 순서를 반복마다 섞었다. 아래 값은 5회 중앙값이며 통계적 유의성 검정은 하지 않았다. +사용자 Mac에서의 측정도 아니다. + +## 결과 1: 느린 작은 소비자가 큰 원본을 붙잡는다 + +첫 소비자가 본문을 바로 출력하고, 두 느린 소비자가 작은 `tag` 문자열을 보관하는 조건이다. +‘관리 공간’은 고정 상태·큐·입력·결과·파서·빈 버퍼 캐시를 포함한 최대 요청 바이트다. 전체 RSS와 다르다. + +| 방식 | 최대 관리 공간 MiB | 명시적 선택 복사 MiB | 전체 완료 시간 ms | +|---|---:|---:|---:| +| 원본 공유 | 4.230 | 0 | 132.60 | +| 소비자마다 복사 | 0.304 | 32.109 | 132.28 | +| 복사본 공유 | 0.297 | 32.055 | 132.28 | +| 크기 비율 2 기준 | 0.297 | 32.055 | 132.24 | +| 크기 비율 8 기준 | 0.297 | 0.055 | 132.18 | +| 마지막 참조에서 복사 | 4.231 | 0.055 | 132.70 | + +크기 비율 8 정책은 빠른 본문 소비자에게 원본을 잠깐 빌려주고 작은 문자열만 독립시켰다. +이 조건에서는 약 **4.23 → 0.297 MiB**로 관리 공간이 줄면서 명시적 복사도 작았다. +이것은 일반 C 코드의 정책 변경으로 얻은 결과다. Pure를 호출하거나 컴파일하지 않았다. + +원본 공유와 마지막 참조에서의 복사는 최대 공간이 거의 같았다. +둘째 소비자가 완료될 때까지는 원본을 반납할 수 없기 때문이다. +원본 활성 바이트·시간의 중앙값은 약 540,593에서 340,108 byte·seconds로 줄었지만, +이미 형성된 최대 유지량은 해결하지 못했다. + +모든 방식에서 최대 큐 개수는 `[1, 64, 64]`였다. 느린 두 소비자의 내부 P99 상한도 각각 +약 5.24 ms, 8.39 ms였다. 이 수치는 입력이 프로그램 밖에서 대기한 시간을 포함하지 않는다. +완료 시간은 합성 지연의 영향을 크게 받으므로 1 ms 미만의 차이를 서비스 속도 개선으로 해석하지 않는다. + +## 결과 2: 소비자마다 복사하면 같은 결과도 중복된다 + +세 소비자가 모두 같은 32 KiB 본문을 선택하도록 바꿨다. + +| 방식 | 최대 관리 공간 MiB | 명시적 선택 복사 MiB | 완료 시간 ms | +|---|---:|---:|---:| +| 원본 공유 | 4.230 | 0 | 133.81 | +| 소비자마다 복사 | 4.296 | 96 | 134.44 | +| 복사본 공유 | 2.262 | 32 | 133.59 | +| 크기 비율 2 기준 | 2.262 | 32 | 133.66 | +| 마지막 참조에서 복사 | 4.512 | 32 | 134.20 | + +소비자별 독립 복사는 원본을 해제하면서도 결과를 중복 보관했다. +**한 번 만든 복사본을 공유하는 기존 기법**으로 복사량과 관리 공간을 줄일 수 있었다. +같은 내용의 모든 문자열을 검색·통합한 것은 아니다. 한 레코드에서 같은 위치와 길이를 선택한 경우만 공유했다. + +늦은 복사는 복사 중 원본과 결과의 공존, 빈 버퍼 캐시 때문에 최대 공간이 더 커질 수도 있었다. +‘마지막 순간에 복사하면 항상 메모리가 작다’는 결론은 성립하지 않았다. + +## 결과 3: 좁은 예산에서는 단순한 정책이 작업 정체도 줄인다 + +본문·패딩 크기가 섞인 JSON을 2 MiB 예산으로 처리했다. +빠른 소비자는 본문을, 느린 소비자들은 작은 문자열을 선택한다. + +| 방식 | 최대 관리 공간 MiB | 완료 시간 중앙값 ms | 측정 범위 ms | +|---|---:|---:|---:| +| 원본 공유 | 2.000 | 516.88 | 516.42~519.74 | +| 복사본 공유 | 1.047 | 134.70 | 134.66~134.73 | +| 크기 비율 2 기준 | 1.047 | 134.65 | 134.60~134.81 | +| 크기 비율 8 기준 | 1.047 | 134.63 | 134.51~135.14 | +| 마지막 참조에서 복사 | 2.000 | 330.51 | 330.33~332.81 | + +큰 원본이 남아 있으면 다음 입력과 파서가 사용할 공간이 부족해진다. +늦은 복사도 정체를 완화했지만, 이 작업에서는 작은 결과를 일찍 분리하는 방법이 더 효과적이었다. +이 수치는 특정 파이프라인의 처리 시간이며 서버나 Pure 전체가 몇 배 빨라진다는 뜻은 아니다. + +큰 본문을 **느린** 소비자에게 보내는 별도 조건에서는 크기 비율 8 정책의 최대 관리 공간이 +4.237 MiB, 비율 2 정책은 2.268 MiB였다. 복사량을 적게 만드는 기준이 메모리까지 항상 최소화하지 않는다. +소비자의 역할과 실제 할당 크기를 같이 봐야 한다. + +## 파서와 풀을 빼면 결과가 왜곡된다 + +메타데이터가 많은 입력에서는 크기 기준 정책의 최대 관리 공간이 약 0.172 MiB였다. +그 실행의 파서 활성 할당 최고치는 약 0.094 MiB였다. +혼합 입력에서는 파서 최고치가 약 0.500 MiB였다. 입력·출력만 계측해서는 이 공간을 설명할 수 없다. +각 영역의 최고치는 발생 시점이 다를 수 있으므로 서로 더해서 총 최고치라고 하지 않는다. + +파서 콜백이 메모리 부족 시 기존 소비자를 진행시키도록 했으므로, +`parse_call_ns`에는 그 진행과 대기 시간이 섞일 수 있다. 순수 파서 CPU 비용으로 사용하지 않는다. +전체 처리 시간·프로세스 CPU·영역별 메모리를 별도로 기록했다. + +또한 빈 버퍼를 보관하는 풀은 할당을 재사용하는 대신 공간을 유지한다. +세 소비자의 다른 속도 조건에서 독립 복사는 풀을 켜면 약 0.304 MiB, +끄면 약 0.241 MiB였다. 풀의 존재만으로 메모리 최적화를 주장하지 않는다. +이번 작은 풀을 각 플랫폼의 최선의 할당기와 비교한 것도 아니다. + +## 검증과 재현 가능한 범위 + +일반 빌드와 ASan·UBSan 빌드에서 총 **108개 검사 그룹**이 통과했다. +파서 자체도 계측해서 빌드했다. 검사는 다음을 포함한다. + +- 1·2·3 소비자의 독립 출력, 큐 슬롯 재사용, Unicode·이스케이프·NUL·빈 문자열. +- 파서 문서 해제 뒤에도 남아 있는 입력/복사본의 유효성, 공유 결과의 마지막 반납. +- 잘린 JSON, 잘못된 필드 타입·누락, 잘못된 UTF-8, 선택하지 않은 필드의 구문 오류. +- 파서 작업 공간이 예산을 넘는 경우의 실패와 반납, 너무 긴 레코드 거절. +- 취소·SIGTERM·실제 출력 실패와 남은 소유권 정리. + +210개 측정 모두 Python JSON 디코더로 만든 소비자별 기대 해시·개수와 일치했고, +모든 측정에서 관리 예산을 지켰으며 종료 후 남은 버퍼 할당은 0이었다. +이 검사가 yyjson의 모든 동작이나 다중 스레드·장치 취소 경합을 검증한 것은 아니다. + +## 남은 한계와 다음 결정 + +이 실험은 실제 파서를 쓰지만 입력·소비자 지연은 합성이고 실행은 한 스레드다. +JSON 한 줄을 모두 읽은 뒤 처리하며 지속 캐시·네트워크·독립적인 요청 도착·GPU를 다루지 않는다. +Sonic·Node.js·.NET 자체의 비교도 아니다. 운영 부하에서 자동 정책이 유리할 가능성까지 기각하지 않는다. + +현재 증거로 결정할 수 있는 범위는 다음과 같다. + +1. 버퍼 수명·역압력·정확한 예산 계측은 **필요한 기반 기능**으로 유지한다. + 자체 ABI나 커널 우회가 이 개선에 필요했다는 주장은 하지 않는다. +2. 공유/복사 정책만으로 Pure의 차별화를 설명하는 이번 비교 연구는 여기서 종료한다. + 남은 특수 조건을 계속 추가해서 후보 정책이 이기는 사례만 찾지 않는다. + 구체적인 새 작업이나 요구 조건이 생기면 보존한 증거부터 검토한다. +3. 다음 조사 후보는 006의 P07/P08, 즉 **큰 문서·상태에서 작은 변경에 필요한 + 계산과 갱신 범위를 줄이는 문제**다. 기존 증분 구현의 잔여 비용을 먼저 확인하고, + Pure가 바꿀 수 있는 경로가 있을 때만 기준 구현을 만든다. 전체 재계산 대비 + 증분 처리의 알려진 이점을 확인하는 것만으로 새 실험을 정당화하지 않는다. + 이 후보에서도 증분 정보를 유지하는 메모리 비용을 포함해야 한다. + +## 코드와 원자료 + +- [실험 설명·실행 방법](../../research/experiments/003-json-fanout/README.md) +- [비교 구현](../../research/experiments/003-json-fanout/benchmark.c), [실행·검증 도구](../../research/experiments/003-json-fanout/run.py) +- [실험 계획](../../research/experiments/003-json-fanout/plan.json) +- [환경·버전·빌드·소스 해시](../../research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/environment.json) +- [입력과 소비자별 출력 해시](../../research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/fixtures.json) +- [검증 기록](../../research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/verification.json) +- [210개 원자료](../../research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/raw.csv), [중앙값·범위](../../research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/summary.json) + +```sh +python3 research/experiments/003-json-fanout/run.py --check-only +python3 research/experiments/003-json-fanout/run.py --output /tmp/pure-json-fanout-results +``` + +새 결과 디렉터리만 허용한다. 의존성은 고정 버전으로 포함되어 있어 재현 시 다운로드가 필요 없다. diff --git a/docs/research/009-bounded-display-integration.md b/docs/research/009-bounded-display-integration.md new file mode 100644 index 0000000..0269d34 --- /dev/null +++ b/docs/research/009-bounded-display-integration.md @@ -0,0 +1,141 @@ +# Pure: 기록을 버리는 실행 계약을 작성물에 연결 + +2026-09-22. **구현·기능 검증 완료. 캐시는 선택 기능으로 유지한다.** + +2026-09-23 후속: **기록 공간을 코드 교체 준비 공간과 공유하도록 구현했다.** +현재 코어는 19,128 B이며, 아래 첫 통합의 20,272 B에서 1,144 B 줄었다. +현재 비용과 검증은 문서 끝의 ‘공유 작업 공간’에 구분해 기록한다. + +이제 기존 `.pure` 작성물의 표시 계산에서 결과를 보관하거나 기록을 지우고, +한 번의 장면 계산에 쓸 작업량을 제한할 수 있다. 소수점 계산·입력 순서·실패 시 +상태 복구는 기존 의미를 보존한다. [앞선 C 코어](../../prototypes/forgetful-core/README.md)의 +정수 DAG를 교체 삽입하지 않고, 실행 계약을 기존 Zig/Wasm 코어에 이식했다. + +## 이번에 끝낸 범위 + +- 전체 장면에 하나의 작업 한도. 부족하면 부분 장면 없이 명시적인 표시 오류. +- 결과와 의존성 요약을 함께 버리는 고정 크기 캐시, 버릴 수 있는 변경 일지. +- 증거가 부족하면 재계산. 입력 이벤트나 외부 효과는 재실행하지 않는다. +- 이벤트 실패, 상태 재정렬, 프로그램 교체, 초기화, signed zero를 포함한 보존 검사. +- 터미널의 `limits`·`forget`·`costs`와 브라우저/Node 공통 API. + +조건부 추적 기록을 다룬 첫 코어와 달리, 현재 작성물의 표시식에는 분기가 없다. +논리 연산도 기존처럼 양쪽을 모두 계산한다. 실제 LOAD에서 상태 의존성을 모으며, +호스트가 전달한 의존성 주석을 신뢰하지 않는다. 캐시는 입력이 없는 상수의 재사용에 +변경 일지를 요구하지 않는다. 코드 교체는 상수 캐시도 폐기한다. + +## 채택 확인: 기존 작성물 세 개 + +같은 새 실행기에서 캐시 0개와 32개를 비교했다. 각 파일마다 관측 100회와 +600프레임, 주기적인 누름·놓음 20회를 재생했다. **총 2,160개 스냅샷이 일치**했다. +이는 정해진 예제 입력 기록이며 사용자 제작 경험이나 대규모 앱 평가가 아니다. + +| 작성물 | 실행된 표시 명령: 캐시 끔 → 켬 | 전체 호출 시간 중앙값 ms: 끔 → 켬 | +|---|---:|---:| +| counter | 9,360 → 93 | 3.111 → 2.693 | +| toggle | 10,080 → 54 | 3.066 → 2.663 | +| charge | 11,520 → 566 | 4.587 → 4.100 | + +시간은 입력 처리와 snapshot 구성까지 포함한 720개 호출 묶음이다. 로딩·컴파일·DOM· +통계 읽기·동등성 검사는 제외했다. 5회씩 순서를 번갈아 측정했으며 Linux AArch64 +한 기기에서 CPU 고정·전력 통제를 하지 않았다. P99 지연이나 macOS 성능이 아니다. +표시 명령이 크게 줄어도 전체 호출 시간의 감소는 이번 측정에서 약 11~13%였다. + +charge의 계측 작업 단위는 오히려 21,600에서 26,909로 늘었다. 변경 기록 확인도 +작업이기 때문이다. 이 단위는 VM 명령 개수나 CPU 시간과 같지 않다. + +## 첫 통합의 비용과 결정 — 별도 기록 공간을 둔 빌드 + +| 영역 | 이전 → 현재 | +|---|---:| +| 코어 고정 구조 | 19,072 → 20,272 B | +| Wasm 파일 | 48,182 → 53,125 B | +| Wasm 선형 메모리 용량 | 65,536 → 65,536 B | + +**1,200바이트의 추가 기록 공간을 소비한다. 메모리를 절감한 결과가 아니다.** +캐시를 꺼도 이 고정 공간은 예약된다. 코드·상태·캐시를 모두 임의로 늘릴 수 있는 +범용 할당기, 기록 공간의 실제 반납, 큰 문서의 의존성 표현은 구현하지 않았다. + +따라서 작업량 제한은 기본 적용하되 캐시는 기본으로 켜지 않는다. 작은 예제의 +이득만으로 모든 작업의 기본 정책을 바꿀 근거는 부족하다. 사용자는 CLI나 임베딩 +API에서 선택할 수 있다. 현재 한도에서는 기본 작업량 2,048로 모든 정상 표시식을 +완료할 수 있다. 실제 응답 시간이나 앱 전체 작업량의 보장은 아니다. + +이 단계의 완료 기준은 **실제 작성물로 연결, 기존 의미 보존, 한도 초과·복구 검증, +추가 비용 확인**이었다. 모두 확인했으므로 같은 예제의 측정 확장은 하지 않는다. +학술적 신규성·대규모 서비스 우위·Pure 전체 완성은 이번 결과의 주장이 아니다. + +## 검증과 재실행 + +Zig 네이티브 20개와 JavaScript/CLI/Wasm 45개 검사가 통과했다. 이 통합은 Zig의 +검사와 실제 Wasm 실행으로 검증했다. 앞선 C 코어의 ASan·UBSan 결과를 이번 +Zig 변경의 검사로 대신하지 않는다. + +실제 Chromium에서도 작성·수정·기록 삭제·한도 초과·복구의 11개 확인을 통과했다. +UI의 한도 초과 검사는 테스트 세션의 모듈 응답에서 기본 작업량만 1로 바꿔 +진입했으며 제품 파일은 바꾸지 않았다. 검사 뒤 원래 설정과 빈 화면으로 복원했다. + +- [통합 소스](../../prototypes/workbench/evaluation.zig), [실행기](../../prototypes/workbench/web/runtime.mjs) +- [사용 방법](../../prototypes/workbench/README.md), [정확한 계약](../../prototypes/workbench/CONTRACT.md) +- [빌드·검사·소스 해시](../../prototypes/workbench/results/2026-09-22-evaluation/verification.json) +- [비교 원자료](../../prototypes/workbench/results/2026-09-22-evaluation/comparison.json) +- [첫 통합의 브라우저 확인 절차](../../prototypes/workbench/results/2026-09-22-evaluation/sources/qa-evaluation.js) +- [브라우저 결과](../../prototypes/workbench/results/2026-09-22-evaluation/browser.json), + [한도 초과 화면](../../output/playwright/pure-evaluation-limit.png) + +```sh +node prototypes/workbench/cli.mjs prototypes/workbench/examples/charge.pure +node prototypes/workbench/evaluation.test.mjs +node prototypes/workbench/evaluation-check.mjs --output /tmp/pure-evaluation-new.json +``` + +## 공유 작업 공간 — 2026-09-23 + +평소의 계산 기록과 교체할 코드를 준비하는 공간이 동시에 필요하지 않다는 +점을 이용했다. 두 자료를 한 공간에 번갈아 담고, 현재 실행 중인 프로그램과 +상태는 별도로 유지한다. 교체 준비 중에는 기존 표시식을 재계산한다. +교체를 시작한 뒤 발생한 입력·초기화도 새 프로그램에 상태를 보존할 때 반영한다. +교체가 끝나거나 취소되면 같은 공간을 빈 계산 기록용으로 돌려준다. + +| 항목 | 별도 공간 → 공유 공간 | +|---|---:| +| 전체 코어 고정 구조 | 20,272 → **19,128 B** | +| 기능 추가 전 19,072 B 대비 증가량 | 1,200 → **56 B** | +| Wasm 파일 | 53,125 → 53,952 B | +| Wasm 선형 메모리 | 65,536 → 65,536 B | + +56 B는 제어 정보 48 B와 Zig의 유니언 안전성 확인용 8 B다. 캐시·변경 기록 +1,152 B는 기존 9,392 B 준비 공간 안에 들어간다. 코어 자료구조에서 1,144 B를 +줄였지만 Wasm 파일은 827 B 늘었다. OS 페이지 반납이나 프로세스 전체 RAM의 +절감을 측정한 결과는 아니다. 첫 통합의 시간 측정도 이 빌드의 속도 증거로 재사용하지 않는다. + +이전 빌드와 771개 스냅샷에서 상태·출력·진단 및 표시 작업량·명령 수·캐시 적중이 +일치했다. 입력·초기화·재적용·기록 삭제를 섞었다. 별도로 교체 준비 중 기존 이벤트와 +표시를 실행하고, 512개 명령을 준비하는 동안 설정·기록 삭제·초기화를 반복해 +교체할 코드가 훼손되지 않는지 검증했다. 거절·취소·중복 취소도 확인했다. +Zig 네이티브 23개, JavaScript/CLI/Wasm 47개 검사가 통과했다. +최신 Wasm을 실제 Chromium에 올린 12개 확인도 통과했다. 작성·수정·기록 삭제· +한도 초과·복구와 공유 공간의 크기를 확인했으며, 처리되지 않은 브라우저 오류는 없었다. +UI 한도 초과는 첫 통합과 같은 테스트 전용 모듈 응답 변경으로 유도했고, +검사 뒤 원래 설정과 빈 작업물로 복원했다. + +트레이드오프는 코드 교체를 시작하면 캐시를 잃는다는 것이다. 실패·취소하더라도 +캐시까지 보존하지 않으며, 아주 작은 작업 한도는 재계산에 부족할 수 있다. +현재 프로그램·상태·이벤트 진단은 보존한다. 동시 스레드나 비동기 재진입은 지원하지 않는다. + +- [현재 빌드·검증 기록](../../prototypes/workbench/results/2026-09-23-workspace/verification.json) +- [이전 빌드와 비교한 원자료](../../prototypes/workbench/results/2026-09-23-workspace/comparison.json) +- [보존한 이전 소스·Wasm 목록](../../prototypes/workbench/results/2026-09-22-evaluation/sources/manifest.json) +- [현재 Wasm에서 교체 중 실행 검사](../../prototypes/workbench/workspace.test.mjs) +- [현재 브라우저 검사 절차](../../prototypes/workbench/qa-evaluation.js), + [12개 확인 결과](../../prototypes/workbench/results/2026-09-23-workspace/browser.json), + [한도 초과 화면](../../output/playwright/pure-workspace-limit.png) + +```sh +node prototypes/workbench/workspace.test.mjs +node prototypes/workbench/workspace-check.mjs --output /tmp/pure-workspace-new.json +``` + +이로써 표시 계산 계약의 통합과 그 추가 공간을 줄이는 후속 구현을 마무리한다. +이 기능의 합성 측정은 더 늘리지 않는다. 큰 앱과 문서의 메모리 문제는 +별도의 실제 작업으로 확인해야 하며 이번 결과로 해결했다고 주장하지 않는다. diff --git a/docs/research/010-roadmap.md b/docs/research/010-roadmap.md new file mode 100644 index 0000000..a9ae94d --- /dev/null +++ b/docs/research/010-roadmap.md @@ -0,0 +1,288 @@ +# Pure: 작은 OS로 설계하는 네이티브 애플리케이션 런타임 + +수정일: 2026-09-23. 상태: 연구·구현 방향. 새 아키텍처의 성능이나 신규성을 입증한 문서가 아니다. + +## 목적과 이번 수정 + +**Pure는 하나의 작은 OS로 설계하는 독립 네이티브 애플리케이션 런타임이다.** +**마이크로 런타임의 크기 제약을 최상위 조건으로 유지한다.** 항상 필요한 실행·소유권·자원· +완료 규칙을 코어에 두고, 프로토콜·저장 구현·UI·미디어는 앱이 필요한 구성만 결합한다. +전체 기능을 기본 탑재하거나 영역마다 관리 서비스·스레드·큰 큐를 예약하지 않는다. +코어·선택한 전체 빌드·유휴 메모리·영역과 진행 작업별 비용을 확인하며, 의존성으로 옮긴 비용도 포함한다. +실행·스케줄링·메모리·보호·자원 수명·저장·통신의 운영 모델을 Pure가 소유한다. +입출력·연산·상태 사이의 연결을 효율적으로 표현하고 실행하는 것이 중심 목적이다. +현재 호스트 OS와 그 프로세스는 Pure를 구동할 때 받는 수단·제약이며 앱의 기본 모델이 아니다. +프로세스 배치 관리에서 출발하거나 작은 OS라는 목표를 장기 부가 기능으로 미루지 않는다. + +**Pure 외부와의 통신도 핵심 요구사항이다.** 일반 웹 서비스·다른 런타임·외부 프로그램과 +상대의 프로토콜·형식으로 요청을 주고받을 수 있어야 한다. Pure끼리의 자체 형식을 모든 +상대에게 요구하지 않는다. 표준 호환 접점의 의미와 비용을 지원하면서 내부 운영 모델은 Pure가 정한다. + +net·RPC·DB·UI는 런타임이 연결하는 관점에서 모두 +입출력과 그 안의 연산을 가진 영역이다. 핵심 설계 대상은 어디의 어떤 데이터를 어떤 연산과 +출력에 연결하며, 어떤 표현·소유권·수명·실행 시점으로 그 연결을 수행할지다. + +서버·CLI·데스크톱 앱이 같은 기반을 사용하며, 화면 없는 앱에 UI 모듈이나 프레임 루프를 +요구하지 않는다. 이 문서는 목표와 연구 방향이며 지원 완료나 성능 우위의 선언이 아니다. + +**SQL을 사용하지 않는다.** 여기서 DB는 데이터 접근·갱신·영속성의 범위를 뜻한다. +RAM·storage를 테이블·행·SQL 실행 모델에 맞추지 않고, 소비자가 필요한 범위와 연산·갱신· +수명에서 데이터 배치를 정한다. 단순히 자체 쿼리 문법이나 범용 객체 저장소로 교체하는 것도 +목표가 아니다. 저장 위치가 달라도 필요한 데이터를 연결하되, RAM의 쓰기 완료와 영속화 +완료가 같다고 취급하지 않는다. + +효율적인 인터페이스와 그 아래의 불필요한 추상화 제거는 전체 애플리케이션 경로에 적용한다. +기존 앱·프레임워크·런타임의 구조나 개념을 필수 전제로 삼지 않는다. 첫 UI 실험의 범위를 +제품 전체의 정의로 확대한 것은 Codex의 오류다. UI를 완성한 뒤 net/RPC/DB를 부가 기능으로 +붙이는 순서도 철회한다. 반대로 데이터 처리 도구만으로 UI 사용성을 검증한 것으로 세지 않는다. + +웹과 같은 유연한 작성·연결·갱신 경험을 제공하되 브라우저를 필수 실행 기반으로 삼지 않는다. +표준 포맷·웹 호환성은 유지한다. 017에서 웹 호환성을 브라우저 소유의 레이아웃·편집으로 +해석한 것은 잘못이며, 해당 실험으로 네이티브 UI 기반이 불필요하다는 결론을 내릴 수 없다. + +효율은 사용자의 표현·조작 비용과 기계의 메모리·계산·전달 비용을 함께 뜻한다. +입력에서 실제 출력까지 필요한 표현·변환·복사·대기·수명 관리에서 설계를 시작한다. +현재 DSL, VM, 상태 객체, 이벤트 버스, 뷰 트리, 렌더러 경계도 변경 가능한 구현 선택이다. +파일을 합치거나 추상화에 새 이름을 붙인 것으로 비용이 제거됐다고 판단하지 않는다. +009는 작은 코어의 계약과 공간 재사용을 검증했다. 전체 인터페이스 경로의 효율은 아직 검증하지 않았다. + +## 연결에서 해결할 문제 + +사용자는 필요한 입력·연산·상태·출력의 관계와 관찰 가능한 동작을 표현한다. Pure는 그 의미를 +보존하는 실행 경로에서 중간 표현·전달·보관·대기를 줄일 수 있는지를 다룬다. 연구 단위는 +개별 API의 호출 비용뿐 아니라 여러 경계를 지나는 한 작업의 전체 데이터·실행 경로다. + +- 연결: 실제로 필요한 값·범위·변경과 그것을 소비하는 연산·출력은 무엇인가? +- 표현과 이동: 어떤 변환이 필요한가? 같은 데이터를 다시 포장하거나 복사하는 단계는 제거 가능한가? +- 보관과 수명: 누가 언제까지 데이터를 쓰는가? 공유·이동·복사·재계산 중 전체 비용에 맞는 선택은 무엇인가? +- 실행과 전달: 언제 계산하고 공개해야 하는가? 합치거나 미루거나 생략해도 결과·순서·기한이 유지되는가? + +이 질문은 설계 기준이다. 현재 구현이 모든 연결의 최적 경로를 자동으로 찾는다는 뜻은 아니다. +예를 들어 DB 결과의 일부만 화면과 RPC 응답에 필요할 때, 필요한 부분을 전달하는 경로와 +전체 결과를 여러 객체·버퍼로 바꾸는 경로를 같은 동작으로 비교할 수 있다. 저장·전송 포맷이 +요구하는 변환과 비동기 소비자의 수명도 비용에 포함한다. API 모양을 통일한 것만으로 성공으로 세지 않는다. + +## 연결되는 영역과 보존할 의미 + +| 범위 | 책임 | +|---|---| +| 공통 실행 기반 | 앱 로직·상태, 작업 실행·스케줄링, 데이터와 자원의 소유권·수명·예산, 비동기 완료·취소·오류, 대기열 한도와 과부하 처리 | +| net | 연결·송수신·스트림·프로토콜 연결, 부분 입출력과 버퍼 수명, 느린 상대와 연결 종료 처리 | +| RPC | 요청·응답·스트리밍, 인코딩과 호출 식별, 기한·원격 실패·재시도와 중복 실행의 의미 | +| 데이터·영속성 | 필요한 범위의 접근·갱신·일관성, 참조의 수명, 변경 공개·내구성·복구의 경계; SQL·관계형 스키마를 내부 모델로 요구하지 않음 | +| UI | UI 표현·레이아웃·텍스트·입력 대상 판정·포커스·캡처·스크롤·렌더링; 공통 실행 기반을 사용하는 하위 체계 | +| 플랫폼 연결부 | Pure의 운영 정책을 실행하는 자원 수단·완료·오류·지원 한도; 현재 호스트의 메모리·시계·실행·저장·통신·UI 접근을 연결 | +| 표준 호환 경계 | 기존 문서·미디어의 읽기·쓰기와 외부 환경 연결; 내부 표현과 변환·복사 비용을 명시 | +| 선택 가능한 웹 호스트 | 배포·호환·검증 경로 중 하나; 네이티브 애플리케이션의 필수 실행 의존성이 아님 | + +이 표는 연결에 참여하는 영역의 의미를 정리한 것이다. 기능별 엔진을 먼저 완성하고 나중에 +접착 계층을 추가하는 구현 순서가 아니다. 계층 수·패키지 구조·하나의 범용 이벤트 버스를 고정하지 않는다. +필요한 기능만 연결할 수 있어야 한다. DB 지원을 곧 새 저장 엔진 개발로, net 지원을 곧 자체 +네트워크 스택 개발로 확정하지 않는다. 기존 구현을 연결하더라도 앱에서 쓸 수 있는 실행· +수명·오류 계약까지 제공해야 하며 외부 라이브러리 이름만 나열한 것으로 지원을 세지 않는다. + +공통 계약의 후보는 [연산 의미](../design/001-operation-semantics.md)와 +[메모리·수명](../design/002-memory-lifetime.md)에 이미 있다. 구현·검증 상태를 확인해 +재사용하거나 수정한다. 모든 기능을 현재 숫자 VM에 넣지는 않는다. UI는 일관된 조작 의미를 +소유하면서 글꼴·래스터화 등의 전문 구현 및 OS의 입력·접근성 서비스와 연결한다. + +## 다음 연구 질문과 첫 작업물 + +> 필요한 입출력과 연산의 관계에서 실행 경로를 구성할 때, +> 관찰 가능한 동작과 각 영역의 의미를 유지하면서 +> 표현·이동·보관·대기의 전체 비용을 얼마나 줄일 수 있는가? + +- 바꿀 결정: 어떤 표현을 권위 있는 원본으로 두고, 어떤 중간 표현·계층을 없애거나 필요할 때만 생성할 것인가? +- 남은 질문: 전체 입력→출력 경로에서 중복 상태·변환·갱신과 관리 비용이 실제로 어디에 집중되는가? +- 최소 확인: 같은 작업물의 변경을 로컬 조작과 RPC로 요청하고 DB에 저장한 뒤, 확정된 결과를 응답과 UI에 반영하는 경로. UI 없이도 앱 로직·RPC·DB가 동작해야 한다. +- 채택 기준: 같은 표현력·관찰 가능한 동작에서 경로 전체의 비용이 줄고 사용자 작성·수정이 가능해야 한다. +- 중단 기준: 튜닝한 단순 구현과 차이가 없거나 표현력·정확성·반응성을 희생해야 하면 해당 가설을 닫는다. + +기존 첫 작업물은 사용자가 대상·값·연결·입력 동작을 정의할 수 있는 작은 공간형 작업물이다. +예를 들어 데이터 항목을 두 영역에 배치하고 키보드나 드래그로 이동하며 연결된 값·표시를 바꾼다. +실행 중 연결·표현을 수정하고 저장·복원할 수 있어야 한다. +새 동작을 만들 때 코어 수정이 필요하면 제작 도구로서의 기준을 통과하지 못한다. +현재 작성 도구에서 이어 만들 수 있는 부분은 재사용하고 별도 조작 데모를 반복해서 만들지 않는다. + +다음 작업은 기존 작성물의 변경·저장 기능을 애플리케이션 경로로 확장하는 것이다. +로컬 조작 또는 네트워크 RPC → 앱의 변경 처리 → DB 저장 → 응답·표시를 하나의 실제 +작업으로 연결한다. 먼저 요청 데이터·작업·버퍼의 수명과 실패 경계를 정하고 필요한 범위만 +구현한다. 이는 제안하는 첫 검증 작업이며 제품 전체를 공동 편집기로 한정하는 결정은 아니다. + +네이티브 UI 경로도 이 작업의 일부다. 창 크기와 배율이 바뀌면 Pure의 배치 결과에서 렌더링과 +입력 대상 판정이 함께 갱신되어야 한다. UI 없이도 동일한 앱의 변경·저장·요청 처리를 사용할 +수 있어야 한다. 모의 RPC·메모리 배열 저장만으로 net/DB를 검증했다고 세지 않는다. + +입력→변경 확정→계산→출력 전체를 첫 단계부터 연결한다. +대상 하나의 변경에 전체 작업물의 호스트 객체·스냅샷·출력 표현을 다시 만드는지 확인하고, +그 비용을 제거하는 후보와 같은 기능의 단순 직접 구현을 비교한다. +필수 데이터, 계산 기록, 출력 자료, 편집 자료의 비용을 구분한다. + +## 설계 자유와 지켜야 할 의미 + +하나의 원본 정의에서 실행에 필요한 접근·갱신 경로를 준비하고 중복 해석과 모델 동기화를 줄이는 +방식을 후보로 둔다. 그래프·트리·바이트코드는 필요성이 확인되면 선택한다. +사용자가 보는 표현과 CPU/GPU에 유리한 배치가 같아야 한다고 강제하지 않는다. +파생 표현은 생성·동기화·저장 비용보다 이익이 있을 때 유지한다. + +입력을 같은 연결 규칙에서 다루되 의미 차이는 보존한다. 버튼 누름·해제처럼 빠뜨리면 동작이 +달라지는 사건과 최신 위치를 사용할 수 있는 연속 값은 같은 버리기 정책을 쓰지 않는다. +출력도 GUI·TUI·장치의 지원 기능을 명시하고, 공통 모델 때문에 최소 공통 기능에 가두지 않는다. +지원하지 않는 기능을 조용히 다른 의미로 바꾸지 않는다. + +경계를 없애거나 공간을 공유하더라도 소유권, 효과 실행 순서, 비동기 완료와 자원 수명을 정의한다. +공통 실행 계약이 외부 효과까지 원자적으로 되돌려 주는 것으로 표현하지 않는다. 설계에서 +취소 요청과 실제 완료, RPC 응답 유실과 원격 실행 실패, DB 커밋과 화면 반영을 구분한다. +재시도·중복 제거·복구의 보장 범위를 명시하고, UI 규칙의 숫자 상태 롤백을 재사용해 대신하지 않는다. +격리나 플랫폼 접근 제한을 없앤 것으로 효율을 만들지는 않는다. +커널 우회·자체 ABI도 금지된 선택이 아니다. 없애려는 구체적인 경로와 비용이 근거가 되어야 한다. +현재의 캐시·공유 공간 구현도 목표 달성을 방해하면 교체한다. + +## 단계와 완료 조건 + +| 단계 | 산출물 | 완료 기준 | +|---|---|---| +| 1. 네이티브 애플리케이션 경로 | 기존 작업의 로직·실제 net/RPC·DB와 네이티브 UI를 필요한 범위로 연결 | UI 없는 실행도 가능; 완료·취소·저장 실패·연결 종료에서 결과와 수명을 설명하고 브라우저 의존 없이 동작 | +| 2. 실용 앱 완성 | 같은 앱의 작성·조회·변경·저장·복구, UI의 텍스트·스크롤·편집 | 실행 중 수정, RPC 기한·재시도, DB 트랜잭션·재시작, IME·포커스·접근성을 실제 사용 흐름에서 확인 | +| 3. 규모 확대 | 동시 요청·큰 데이터·느린 소비자·지속 실행과 UI 부하 | 전체 메모리·대기열·지연·처리량·누락을 실제 작업에 맞춰 측정하고 기능별 비용을 설명 | +| 4. 환경·분야 확대 | 추가 네이티브 환경·프로토콜·DB·미디어·웹 호환 경로 | 공통 계약의 재사용과 분야별 의미·표현력·호환성을 함께 유지 | +| 5. 운영 규모 검증 | 다중 사용자·장애 복구·배포와 장기 실행 | 충돌·과부하·권한·격리·내구성 보장을 검증한 범위에서 서비스 규모를 주장 | + +앞 단계의 작업물을 발전시킨다. UI·서버·CLI 중 첫 구현의 편의가 전체 제품 범위를 결정하지 +않도록 한다. 현재 Zig 실행 코어와 웹 호스트는 부분 계약을 검증한 구현이다. 통합 net/RPC/DB와 +네이티브 UI 경로는 아직 구현하지 않았다. 기존 코어의 재사용 여부도 새 요구에 따라 판단한다. +네이티브 숫자 코어 테스트나 독립 데이터 처리 실험만으로 애플리케이션 런타임 완료로 세지 않는다. + +첫 목표 환경은 사용자가 밝힌 M칩 macOS다. 현재 작업 환경인 Linux의 빌드·검사 결과로 +macOS의 창·Metal·IME·접근성 동작을 검증했다고 주장하지 않는다. 플랫폼 선택은 런타임의 +애플리케이션 의미와 분리하고, 실제 macOS 검증이 남으면 완료 보고에 명시한다. + +## 측정과 규모 선택 + +입력·요청 도착부터 작업 결과·RPC 응답·DB 커밋·화면 제출까지 해당 경로의 지연을 구분해 잰다. +UI가 없는 작업에 화면 지표를 요구하지 않는다. 실제 화면 표시 시점을 확보할 수 없는 +환경에서는 제출 시점과 구분한다. 큐 대기, 취소·누락, 편집 적용 비용도 포함한다. +데이터 복사, 파생 객체 생성, 동기화할 표현의 수, 살아 있는 바이트, 관리 정보, +호스트·GPU·I/O 버퍼·DB 캐시·드라이버·전체 프로세스 비용을 기록한다. 외부 DB 서버의 비용을 +클라이언트 밖으로 옮겨 절감으로 세지 않는다. 같은 물리 공간을 중복 합산하지 않는다. +컴파일러 배포 크기·빌드 시간·피크 RAM·산출물 크기도 별도로 관리한다. + +첫 크기는 실제 상호작용과 필수 상태 크기로 정한다. 대상 수와 함께 관계 수, +변경 비율, 보이는 범위, 계산 비용을 기록한다. 이전 안의 100만 행·128 MiB를 +인터페이스의 보편적 합격 기준으로 고정하지 않는다. +사용자 작업과 목표 기기의 제약으로 예산·응답 목표를 정하고 후보 측정 전에 확정한다. + +같은 상호작용을 잘 구현한 직접 경로와 비교하며 기존 구현에도 부분 갱신·재사용을 허용한다. +특정 계층의 비용을 호스트·드라이버·디스크로 옮긴 것만으로 성공으로 세지 않는다. +대용량 질의가 실제 병목이 될 때만 해당 데이터 도구와 추가 비교한다. +선행연구는 중복·과장을 피하기 위해 확인하되 기존 아키텍처 채택을 강제하는 기준으로 쓰지 않는다. + +다음 착수 항목은 **기존 작업의 변경·저장을 기준으로 UI·RPC·DB가 공유할 실행·수명 계약을 +구체화하고, 그 계약을 쓰는 네이티브 애플리케이션 경로를 구현하는 것**이다. 범용 기반을 +한꺼번에 만들지 않고 한 요청의 접수·실행·외부 효과·완료·자원 회수부터 연결한다. +각 기능의 지원 범위와 남은 의존성을 기록하고, 실제 병목과 비교 선택지가 있을 때 측정한다. +공통 API나 직접 렌더링을 만들었다는 사실만으로 메모리 효율이나 신규성을 주장하지 않는다. + +현재 진행 — [019](019-byte-storage.md): [018](018-native-connections.md)에서 잘못 도입한 +SQLite를 제거하고 실제 TCP → 바이트 저장 → 전송 경로로 대체했다. 호출자의 본문을 그대로 +저장하고 필요한 범위를 매핑해 전송까지 유지한다. 버전 충돌·동시 쓰기·부분 전송·갱신 중 +이전 참조·공개 전후의 실패·프로세스 종료·재시작을 확인했다. 전체 성능 우위나 최종 데이터 +배치를 결정한 것은 아니다. + +후속 [020](020-prepared-connections.md): `.pure`로 저장·응답할 구간을 작성하고 서버 시작 때 +별칭·중첩 구간을 최종 접근 범위와 원본 길이 조건으로 준비한다. 실제 TCP에서 작성 변경과 +오류·효과·원본 수명을 확인했다. + +후속 [021](021-field-connections.md): 경계의 필드를 이름으로 선언하고 실제 입력 숫자에 따라 +선택할 구간을 계산한다. 한글·이모지 문서에서 선택한 내용만 저장·응답하는 네이티브 경로를 +확인했다. 필드 배치·별칭은 준비 때 해소하고 데이터 의존 단계만 실행한다. 현재는 u32le· +고정 바이트·가변 꼬리 필드와 입력 기반 범위에 한정되며 일반 레코드 런타임은 아니다. +다음에는 앱의 읽기·변경·저장 경로에 필요한 연산·버전 관계를 다룬다. 데이터 전체를 객체로 +재구성하지 않으면서 서로 다른 생산자의 값·수명을 연결할 필요가 있는지 실제 작업에서 결정한다. +필드 종류나 예제 숫자를 늘리는 것만으로 진행하지 않는다. 일반 피드백·동적 분기·여러 효과는 별도 계약과 +검증이 필요하며 전역 최적 경로를 자동으로 찾는다고 주장하지 않는다. +네이티브 UI와 범용 RPC·데이터 실행 모델까지 구현한 것은 아니므로 1단계 전체 완료로 세지 않는다. + +통신 방향 추가 — [자체 통신 형식 설계](../design/006-native-protocol.md): HTTP는 호환 경계이며 +Pure 간 통신의 필수 모델이 아니다. 작성된 연결·필드에서 바이너리 형식을 준비하는 방향으로 +진행한다. 먼저 현재 get/put 전용 진입점을 앱 입력·효과와 분리해 같은 저장 작업을 연결한다. +이는 위의 읽기·변경·저장 확장에 앞서 정리할 경계다. 이후 제한된 scalar 피어 +[026](026-secure-peer-link.md)와 같은 문서 작업의 인증 피어 [030](030-authenticated-document-peer.md)에서 +TLS 1.3/TCP를 선택 전송으로 구현했다. 범용 전송 정책·QUIC은 미결정이다. 새 자체 전송은 +구체적인 병목과 동등한 보안·전달 조건에서 판단한다. 현재 평문 HTTP나 제한된 TLS 피어의 +결과로 QUIC 대비 우위를 주장하지 않는다. + +운영 모델 정정 — [작은 OS로서의 Pure](../design/007-execution-domains.md): Pure 코어가 실행· +자원·권한·연결·수명을 관리하고 플랫폼 연결부는 현재 환경의 수단·결과·제약을 제공한다. +다음 진입점 분리에서 요청 접수·진행·완료·회수의 소유자를 이 코어로 정하고, 기존 저장 +작업을 내부 연결과 외부 입력 양쪽에서 실행한다. 실행 영역은 Pure 내부 단위이며 프로세스 +관리자의 포장으로 만들지 않는다. 호스트의 자원 부족·입출력 실패도 실제 검증 범위에 포함한다. +원격 형식과 영역별 스레드·소켓·VM을 필수로 요구하지 않는다. 직접 소켓 구현의 낮은 추가 +비용과 Kitex 수준의 RPC를 목표로 삼되, 동등한 작업·보장으로 확인하기 전 성능 달성을 +주장하지 않는다. 통합 OS 코어·영역 관리자·샌드박스·직접 하드웨어 구동은 아직 미구현이다. + +후속 [022](022-execution-core.md): 저장 프로필에 한정된 실행 영역의 작업 슬롯·접수 예산· +준비 대기열·완료·결과 대여를 구현하고 기존 HTTP 서버를 연결했다. 내부 결과 전달은 소켓· +직렬화·추가 본문 할당 없이 원본의 수명을 유지한다. 실제 파일 저장·갱신 및 기존 TCP 회귀와 +sanitizer 검증을 통과했다. 위의 미구현 항목 중 영역별 기본 수명 관리를 일부 구현한 상태이며 +통합 OS·일반 앱 작성·자체 보안 RPC·네이티브 UI 완료는 아니다. +첫 공개 목표와 남은 요건은 [체크포인트](../../CHECKPOINT.md)에서 유지한다. + +후속 [023](023-document-edits.md): 같은 저장 프로필에 조건부 바이트 부분 변경을 추가했고 +네이티브 CLI와 HTTP가 같은 준비된 효과·버전을 사용한다. 큰 문서의 작은 변경은 변경 구간만 +입력하지만 저장소의 전체 파일 재작성 비용은 남는다. core/editor/server 선택 빌드와 release +코어의 크기·의존성 회귀 검사를 추가했다. 일반·sanitizer 빌드의 36개 통합 시나리오를 통과했다. +현재 코어의 작은 수치로 전체 앱 메모리나 범용 런타임 완성을 주장하지 않는다. 다음 작업도 +작성 모델·실제 네이티브 앱·보안 통신의 남은 연결을 대상으로 하며 기능 목록을 늘리지 않는다. + +후속 [024](024-native-app-instances.md): 기존 Workbench 계산을 명시적인 인스턴스로 분리하고, +같은 .pure를 네이티브 CLI로 빌드했다. 입력·스냅샷·조건부 저장·복원이 공통 C 코어를 통과한다. +현재 scalar 모듈의 한도와 앱별 고정 예약 비용은 남는다. 공통 코어에는 VM·컴파일러를 추가하지 +않았다. 실제 창, 실시간 작성 적용, JSON 프로젝트 변환, 보안 RPC·비동기 효과·규모 검증은 +계속 체크포인트의 미완료 항목이다. + +## 이전 구현 기록 + +아래 기록의 당시 ‘다음 작업’은 이 문서 앞부분의 애플리케이션 런타임 범위·우선순위보다 우선하지 않는다. + +진행 기록: [011](011-direct-presentation.md)에서 현재 작성 도구의 전체 장면 객체와 DOM 재생성 +경로를 제거했다. 결과·오류·작성 흐름은 보존했지만 속도·전체 RAM 개선은 입증하지 못했다. +공간형 직접 조작이나 다양한 출력의 구현까지 완료한 것으로 세지 않는다. + +후속 [012](012-refresh-ownership.md): 프로파일에서 실행 입력마다 편집기 정보를 갱신하던 비용을 +확인하고 연결을 제거했다. 실제 작성 흐름을 보존하면서 동일 조건의 동기 입력 처리 시간을 줄였다. +원격 서비스·대규모 작업이나 실제 화면 표시 지연의 우위로 일반화하지 않는다. + + +후속 [013](013-authored-pointer-input.md): 대상에 연결한 좌표 입력을 추가하고 이동·제한·취소를 +작성 코드로 처리했다. 실제 마우스·터치·동시 조작·CLI 재생을 검증했다. 속도 비교는 추가하지 않았다. +다음은 배치 결과를 저장·복원하고 여러 대상을 편집하는 하나의 실제 작업물이다. +현재의 소스 저장은 현재 배치 상태를 보존하지 않는다는 제한부터 해결해야 한다. +규모나 성능 측정은 이 작업의 구체적인 병목과 선택지를 확인할 때 수행한다. + + +후속 [014](014-layout-persistence.md): 여러 대상의 배치를 파일로 저장하고 새 실행기에 복원하는 +경로를 구현했다. 적용된 정의·현재 값·미적용 초안을 구분하고 전체 복원을 원자적으로 확정한다. +다음 설계 문제는 대상마다 반복되는 상태·입력·반응 선언이다. 도형 숫자를 억지로 늘린 벤치마크보다, +대상 집합에 하나의 반응을 적용하면서 값과 소유권을 분리할 수 있는 실행 표현을 먼저 검토한다. + + +후속 [015](015-shared-rules.md): record/item의 연속 필드 구간과 한 번만 저장되는 rule 본문을 +연결했다. 편집·선언 순서 변경·동시 조작·저장 복원에서 대상별 값이 유지된다. +현재는 정적인 이름 기반 대상이며 동적 생명주기 핸들은 아니다. 다음 제약은 대상 수만큼 +늘어나는 입력 연결과 키 사건의 모든 연결 순회다. 실제 추가·삭제 작업을 지원할 때 +대상 식별과 선택한 대상만 실행하는 경로를 함께 설계해야 한다. + + +후속 [016](016-pointer-routing.md): 여러 도형을 하나의 포인터 입력에 연결하고 사건의 대상 +필드 구간을 규칙에 전달한다. 대상별 네 반응의 복제를 없애고 접촉 소유권은 따로 유지한다. +키보드 선택은 여전히 작성한 ID 조건에 의존한다. 다음은 선택 없음·대상 제거·저장 복원과 +선택/캡처의 차이를 정의한 뒤 선택 대상에 키 사건을 직접 전달하는 경로다. 정적 대상 번호를 +동적 생명주기 식별자로 취급하지 않는다. + + +후속 [017](017-web-layout-boundary.md): Codex가 웹 호환성 요구를 잘못 해석해 브라우저의 +문서 작업을 우선 구현했다. CSS 레이아웃·textarea·스크롤·표준 UTF-8 파일과 Pure의 숫자 +입력을 연결한 검증은 유효하지만, 브라우저에 UI를 맡기는 구조는 네이티브 목표를 충족하지 +않는다. ‘자체 레이아웃은 필요 없다’는 프로젝트 차원의 결론을 철회한다. 이 호스트의 추가 +최적화는 현재 우선 작업에서 내리고, 파일 호환·입력 정책·취소 동작의 참고 자료로 보존한다. diff --git a/docs/research/011-direct-presentation.md b/docs/research/011-direct-presentation.md new file mode 100644 index 0000000..00f8577 --- /dev/null +++ b/docs/research/011-direct-presentation.md @@ -0,0 +1,102 @@ +# Pure 011: 입력에서 출력까지, 중간 장면 객체 제거 + +작성일: 2026-09-23. 실제 작성 도구의 호스트·출력 경로 변경과 브라우저 검증. + +후속 [012](012-refresh-ownership.md)에서 남은 비용을 프로파일하고 실행 입력과 편집기 갱신의 +연결을 제거했다. 아래 수치는 이 보고서 시점의 구현 결과로 유지한다. + +## 알아낸 것과 사용 경험 + +입력마다 전체 장면 스냅샷과 DOM 요소를 다시 만드는 경로를 제거했다. +계산 결과를 기존 출력 요소에 직접 전달하고 값이 달라진 속성·텍스트만 쓴다. +사용자가 코드를 적용하고 키보드·화면 버튼으로 동작시키는 기존 작성 흐름에 연결했다. +별도의 고정 동작 데모를 만들지 않았다. 작성 중인 코드와 적용된 작업의 구분도 유지한다. + +**중간 표현과 요소 재생성을 없앤 것은 확인했다. 속도 향상이나 전체 RAM 절감은 입증하지 못했다.** +이번 계측에서 새 경로의 시간 중앙값은 오히려 더 컸고, 표본 간 편차도 컸다. +이를 새 범용 아키텍처나 학술적인 신규성으로 주장하지 않는다. + +직접 확인할 동작은 자신이 작성한 값·도형을 입력으로 바꾸고, 정의를 수정하거나 초기화하는 것이다. +입력 갱신 중 같은 SVG 요소를 유지한다. 실제 정의 변경 시에는 출력 구조를 다시 만들 수 있다. +표시 오류가 발생하면 해당 출력을 숨기고, 복구되면 같은 요소를 다시 표시한다. +전체 표시 계산 한도 초과에서는 모든 출력을 숨기되 성공한 입력의 상태는 유지한다. + +## 제거한 것과 남긴 것 + +이전 경로: + + 입력 → Wasm 상태 변경 → 전체 상태·장면 JS 객체 → 새 DOM 요소 → 전체 교체 + +현재 경로: + + 입력 → Wasm 상태 변경 → 표시 계산 검증 → 기존 DOM 목적지에 값 전달 + +`runtime.present(target)`은 상태 맵·장면 객체를 만들지 않는다. +`output.mjs`는 코드 적용 시 선언과 실제 DOM 목적지를 연결하고, 값은 DOM과 비교한다. +독립적인 숫자 상태 복사본이나 가상 장면 트리를 추가하지 않았다. +DOM은 출력을 보관하며 동작을 결정하는 상태는 여전히 Wasm 안에 있다. +저장·CLI·검사용 `snapshot()`은 명시적으로 호출할 때 유지한다. + +작업 한도가 중간에 끝나도 일부 결과만 내보내지 않기 위해 숫자 결과 96개와 유효 표시 24개를 +담는 **792 B의 호스트 버퍼**를 재사용한다. typed array 객체 오버헤드, 적용 시 만드는 binding +참조 배열, DOM 목적지 참조는 별도 추가 공간이다. 이 비용을 Wasm 메모리 수치 안에 숨기지 않는다. +무효한 출력도 DOM 요소를 보관하므로 전부 제거하는 이전 경로보다 해당 상황의 유지량이 늘 수 있다. + +기존 계산 순서·작업량 제한은 유지한다. 매 갱신의 전체 바인딩 평가, 진단 객체 생성, +편집기 부가 정보 갱신까지 없앤 것은 아니다. ‘모든 할당 0’이나 ‘변경 크기에 비례하는 실행’은 아니다. +출력 콜백 중 실행기 재진입은 거절하며, 출력 어댑터는 신뢰된 동기 코드다. +임의 어댑터 예외의 외부 효과까지 원자적으로 되돌리지는 않는다. + +## 실제 브라우저 비교 + +이전 app/runtime 소스를 보존하고, 테스트 세션의 응답만 바꿔 같은 페이지·Wasm·컴파일러에서 비교했다. +24개 출력과 상태 2개를 사용했다. 이동 입력은 x를 0.25 늘리고, 도형 위치와 텍스트 하나만 바꾼다. +각 실행에서 준비 입력 20회 뒤 입력 100회(누름·해제 200사건)를 처리했다. +이전/현재를 번갈아 각각 3회 실행했으며 모든 최종 상태·출력이 일치했다. + +| 관찰 항목, 입력 100회당 | 이전 | 현재 | +|---|---:|---:| +| DOM 요소 생성 호출 | 9,200 | **0** | +| 전체 snapshot 호출 | 200 | **0** | +| 장면 전체 교체 | 200 | **0** | +| 기존 장면 요소의 동일성 유지 | 아니오 | 예 | +| 동기 실행 시간 중앙값 | 648.7 ms | 733.1 ms | +| 동기 실행 시간 범위 | 633.6–941.2 ms | 579.1–751.4 ms | + +현재 장면의 변경은 위치 속성 100회와 값 텍스트 100회였다. 텍스트 노드의 교체까지 +없어진 것은 아니다. 상태 표시와 비용 표시도 요소를 유지하지만 변경된 텍스트는 갱신한다. + +시간은 **브라우저 안의 합성 키보드 dispatch 시작부터 DOM 제출까지**이며 계측 비용을 포함한다. +OS 입력 도착·레이아웃·페인트·실제 화면 표시 시간은 포함하지 않는다. +실제 키보드·포인터 흐름은 별도의 브라우저 조작 검사로 확인했다. +세 표본에서 성능 우위를 주장할 수 없다. 유리한 수치를 얻기 위해 반복 측정을 늘리지 않았다. + +app/runtime/output의 비압축 소스 합은 29,334 → 32,558 B로 **3,224 B 증가**했다. +Wasm 파일 53,952 B, 코어 구조 19,128 B, 선형 메모리 65,536 B는 변경하지 않았다. +호스트 heap·브라우저 전체 RSS·GC 시간·GPU 비용의 절감은 측정하지 않았다. + +## 검증과 재현 + +JS/CLI/Wasm 검사 52개가 통과했다. 새 검사에는 보존한 이전 실행기와 결과·계산량 비교, +0~80 작업 한도, 표시 오류, 이벤트 롤백, 부호 있는 0, 정의 교체, 재진입 거절과 어댑터 예외 복구가 있다. +Zig 소스·Wasm은 변경하지 않았으므로 이전 네이티브 검사를 다시 실행한 것으로 세지 않는다. + +브라우저에서는 새 경로 비교 13개, 기존 작성 흐름 44개, 표시 한도·복구 12개를 확인했다. +후자의 한도 초과는 테스트 전용 모듈 응답에서 한도를 1로 바꿔 유도한다. +검사 후 원래 설정과 빈 작업물로 복원한다. 이전 증거의 이미지는 덮어쓰지 않았다. + +- [변경 전 소스 목록과 해시](../../prototypes/workbench/results/2026-09-23-presentation/before/manifest.json) +- [검사와 현재 소스 해시](../../prototypes/workbench/results/2026-09-23-presentation/verification.json) +- [브라우저 비교 원자료](../../prototypes/workbench/results/2026-09-23-presentation/browser.json) +- [작성 흐름](../../prototypes/workbench/results/2026-09-23-presentation/browser-authoring.json), + [표시 한도](../../prototypes/workbench/results/2026-09-23-presentation/browser-limit.json) +- [실행 계약](../../prototypes/workbench/CONTRACT.md), [재실행 방법](../../prototypes/workbench/README.md) +- [직접 출력 구현](../../prototypes/workbench/web/output.mjs), [실행기](../../prototypes/workbench/web/runtime.mjs) + +## 다음 결정 + +출력 요소 유지와 스냅샷 경로 분리는 실제 작성 도구에 남긴다. 구조 제거의 효과를 관찰할 수 있는 +첫 구현이며, 속도·메모리 효율 가설의 합격 판정은 보류한다. 아직 일반 SDK로 고정하지 않는다. +이 결과만으로 데이터 규모를 키우거나 새 캐시·ABI를 추가하지 않는다. +다음 변경의 근거는 현재 경로의 남은 비용과 더 풍부한 직접 조작의 요구여야 한다. +사용자가 대상·관계를 직접 조작하는 기능과 여러 출력 방식의 의미 보존은 이후 작업으로 남아 있다. diff --git a/docs/research/012-refresh-ownership.md b/docs/research/012-refresh-ownership.md new file mode 100644 index 0000000..d9fde5d --- /dev/null +++ b/docs/research/012-refresh-ownership.md @@ -0,0 +1,100 @@ +# Pure 012: 실행 입력이 편집기를 다시 계산하던 경로 제거 + +작성일: 2026-09-23. 실제 작성 도구 변경, 단계별 프로파일과 브라우저 비교. + +## 알아낸 것과 바뀐 동작 + +011에서 DOM 재생성을 없앴지만 시간이 줄지 않았다. 다음 변경에 앞서 비용을 나눠 측정하니 +출력 계산·전달보다 **바뀌지도 않은 소스 편집기의 정보를 갱신하는 경로**가 더 컸다. +입력 이벤트마다 `refresh()`가 줄 번호, UTF-8 바이트 수, 스크롤, 커서 위치를 갱신했다. +입력과 코드 편집은 독립적인데 하나의 갱신 함수로 묶여 있었다. + +실행 갱신에서 이 경로를 제거했다. 실제 소스 변경에서만 편집기 정보를 갱신하고, +코드 적용은 초안과 적용된 정의의 일치 상태만 갱신한다. +화면에 이미 같은 값이 있으면 상태·진단·버튼 속성도 다시 쓰지 않는다. +새 캐시, 구독 그래프, 이벤트 버스, VM 명령이나 런타임 계층을 추가하지 않았다. + +사용자는 전과 같이 코드를 작성하고, 키보드·화면 버튼·프레임으로 동작시킨다. +실행 중에도 미적용 초안은 그대로 남고 코드 편집·적용·오류 위치 이동의 표시가 갱신된다. +이번 결과는 **불필요한 갱신 연결을 제거한 구현 개선**이다. 새로운 계산 원리의 발견은 아니다. + +## 먼저 확인한 비용 + +24개 출력, 상태 하나, 입력 하나를 작성했다. 이동 입력은 x를 0.25 늘리고 도형과 텍스트에 반영된다. +준비 입력 20회 뒤 입력 100회(누름·해제 200사건)를 처리했다. +기존 app 모듈의 테스트 전용 응답에 타이머를 삽입해 한 차례 프로파일했다. + +| 구간 | 호출 수 | 누적 시간 | +|---|---:|---:| +| 계산·출력 전달 `present` | 200 | 26.6 ms | +| 비용 표시 | 200 | 56.6 ms | +| 편집기 정보 갱신 | 200 | 259.1 ms | +| 전체 `refresh` | 200 | 359.6 ms | + +전체 입력 묶음은 375.9 ms였다. 부분 구간은 refresh 안에 포함되므로 합산하지 않는다. +CDP는 이 구간 주변에서 레이아웃 201회와 레이아웃 시간 177.7 ms를 기록했다. +줄 번호를 쓴 뒤 스크롤 위치를 읽는 경로도 있었다. 이 관찰을 근거로 편집기 갱신 연결을 +먼저 제거했으며, 바인딩 캐시나 출력 평가 정책은 변경하지 않았다. + +## 변경 전후 비교 + +직전 구현의 소스를 보존했다. before 실행에서는 app.js만 테스트 응답으로 교체한다. +런타임·출력 어댑터·컴파일러·Wasm·작성된 소스는 양쪽이 동일하다. +프로파일 타이머는 이 비교에 넣지 않았다. 인코딩 호출과 편집기 DOM 변경을 계측했다. +before/after 순서를 섞어 각각 3회 실행했으며 최종 상태·출력·이벤트 수와 실제 SVG 위치가 모두 같았다. + +| 입력 100회당 관찰값 | 이전 | 현재 | +|---|---:|---:| +| 소스 UTF-8 인코딩 호출 | 400 | **0** | +| 인코딩 결과 배열의 누적 바이트 | 410,800 B | **0 B** | +| 편집기 정보의 DOM 변경 기록 | 800 | **0** | +| CDP 레이아웃 횟수, 매 실행 | 201 | **1** | +| 동기 처리 시간 중앙값 | 837.9 ms | **152.8 ms** | +| 동기 처리 시간 범위 | 709.7–1,151.8 ms | **137.8–327.4 ms** | + +중앙값 기준 약 81.8% 감소다. 세 표본 모두 현재 쪽 시간이 더 짧았다. +다만 표본 수가 작고 편차가 크며 CPU·전력·다른 프로세스를 고정하지 않았다. +이 수치는 동일 세션의 **동기 합성 입력 dispatch부터 DOM 제출까지**다. +실제 키 입력 도착부터 화면 표시까지의 지연이나 큰 앱의 처리량으로 일반화하지 않는다. +계측 자체의 비용도 포함한다. CDP 카운터는 입력 묶음 전후의 브라우저 작업을 포함할 수 있다. +이전 보고서의 별도 실행 시간과 비율을 곱하거나 서로 직접 비교하지 않는다. + +410,800 B는 없어진 인코딩 결과의 누적 용량이다. 피크 RAM 절감량도 전체 복사 바이트도 아니다. +추가 런타임 버퍼는 없다. app.js의 비압축 소스는 16,014 → 16,510 B로 496 B 증가했다. +코어·Wasm·011의 792 B 출력 버퍼와 바인딩 평가 순서는 그대로다. + +## 보존한 계약과 검사 + +소스 입력, Tab 삽입, 코드 조각·파일·예제 삽입은 편집기 정보 갱신 경로를 사용한다. +성공·실패한 코드 적용은 초안 상태를 다시 확인한다. 커서 이동·스크롤·오류 위치 이동은 +해당 표시만 갱신한다. 실행 입력·프레임·초기화는 소스 내용을 재계산하지 않는다. +UI의 오류 문구는 완성된 문자열을 한 번에 갱신하고 기존 사건 오류·표시 오류 구분을 유지한다. + +브라우저 비교·편집 분리 검사 13개, 기존 작성 흐름 44개, 표시 한도·복구 12개가 통과했다. +검사에는 Unicode 바이트 수, 줄 번호, 미적용 초안을 둔 실행·초기화, 성공/실패 적용, +오류 위치 이동, Tab·코드 조각 삽입, 저장·복원, 키보드·포인터·모바일 동작이 포함된다. +Zig·컴파일러·런타임은 변경하지 않았으므로 기존 52개 JS/CLI/Wasm 검사나 네이티브 검사를 +다시 실행한 것으로 세지 않는다. 이번 변경의 검증은 실제 브라우저에서 수행한다. + +- [변경 전 소스·해시](../../prototypes/workbench/results/2026-09-23-refresh/before/manifest.json) +- [현재 소스·검증 기록](../../prototypes/workbench/results/2026-09-23-refresh/verification.json) +- [원인 프로파일](../../prototypes/workbench/results/2026-09-23-refresh/profile-before.json) +- [6회 비교와 편집 분리 검사](../../prototypes/workbench/results/2026-09-23-refresh/browser.json) +- [작성 흐름 검사](../../prototypes/workbench/results/2026-09-23-refresh/browser-authoring.json) +- [표시 한도·복구 검사](../../prototypes/workbench/results/2026-09-23-refresh/browser-limit.json) +- [비교 절차](../../prototypes/workbench/qa-refresh.js), [실행 스크립트](../../prototypes/workbench/qa-refresh.py) + +```sh +python3 prototypes/workbench/qa-refresh.py --cli /path/to/playwright-cli.js --session pure-path --output /tmp/pure-refresh-new.json +``` + +사전에 로컬 페이지를 연 Playwright CLI 0.1.21 세션이 필요하다. 결과 파일은 덮어쓰지 않는다. +검사 종료 후 원래 모듈과 빈 작업물로 복원한다. + +## 결정과 남은 범위 + +실행과 편집의 갱신 경로 분리를 채택한다. 현재 질문에 답했으므로 같은 입력의 추가 측정은 하지 않는다. +이 결과를 ‘모든 추상화 제거가 빠르다’는 근거로 삼지 않는다. 비용이 컸던 것은 실제로 변하지 않은 +편집 자료까지 갱신하도록 연결한 경로였다. 필요한 연결과 수명은 유지한다. +전체 표시식 평가·비용 숫자 포맷·호스트 진단 생성은 남아 있다. 자동으로 다음 미세 최적화에 들어가지 않는다. +다음 인터페이스 확장은 사용자가 대상과 관계를 직접 조작하는 실제 요구를 기준으로 잡는다. diff --git a/docs/research/013-authored-pointer-input.md b/docs/research/013-authored-pointer-input.md new file mode 100644 index 0000000..57d76e9 --- /dev/null +++ b/docs/research/013-authored-pointer-input.md @@ -0,0 +1,109 @@ +# 013 — 작성한 규칙으로 직접 조작하기 + +2026-09-23. 이번 변경은 입력 표현력과 실제 조작 경로의 구현이다. +속도·RAM 비교나 합성 대규모 벤치마크는 수행하지 않았다. + +## 문제와 이번 결정 + +기존 작성물은 키 또는 버튼의 누름·놓음으로만 조작할 수 있었다. +화면에 보이는 대상을 잡고 이동시키려면 좌표 입력이 필요했다. +호스트에 별도 이동 모델을 만들면 작성한 상태와 화면 위치를 동기화해야 한다. +이번에는 좌표를 기존 사건 실행의 일시적인 피연산자로 전달하고, +이동·제한·취소·연결된 출력은 작성한 상태와 표현식으로 처리했다. + +```text +input drag = pointer(tile) +on drag.move { + x = clamp(event_x - grab_x, 20, 564) + y = clamp(event_y - grab_y, 100, 276) +} +``` + +전체 [직접 조작 예제](../../prototypes/workbench/examples/direct.pure)는 +`.down`에서 잡은 위치와 시작 위치를 기억하고, `.up`에서 마지막 위치를 반영한다. +`.cancel`은 시작 위치로 복귀한다. 좌우 키도 같은 x 상태를 바꾼다. +오른쪽 영역에 들어갔는지는 같은 상태를 읽는 다른 출력으로 표시한다. +제한이나 복귀 정책을 바꿀 때 실행기·렌더러 수정은 필요 없다. + +## 구현 경계 + +브라우저는 대상 찾기, 좌표 변환, 포인터 캡처와 해제를 담당한다. +SVG 루트가 캡처를 유지하므로 대상 또는 장면 밖에서도 조작을 끝낼 수 있다. +클라이언트 좌표는 SVG 변환의 역행렬로 장면 좌표에 연결한다. +축소된 모바일 화면에서도 작성 코드는 같은 좌표계를 사용한다. + +코어에는 좌표 읽기 명령 두 개와 `dispatch_point(event,x,y)`가 추가됐다. +좌표는 호출 동안만 존재하며 영구 상태 슬롯을 만들지 않는다. +기존 명령·키 입력 번호와 사건의 원자적 확정, 변경 기록, 출력 경로는 유지한다. +호스트의 소유권 기록은 활성 포인터 입력마다 출처와 마지막 좌표를 가진다. +조작 위치의 권위 있는 값은 작성한 상태에만 있다. + +하나의 입력은 한 포인터가 소유하며 다른 대상에는 독립적인 접촉을 허용한다. +놓기는 마지막 좌표를 반영한다. 취소·캡처 상실·초점 상실은 별도 cancel 사건이다. +이미 종료된 포인터의 늦은 놓기나 이동이 두 번째 사건을 만들지 않는다. +새 큐, 프레임 단위 묶기, 최신 좌표만 남기는 정책은 추가하지 않았다. +브라우저가 전달한 move를 처리하며 하드웨어의 모든 샘플 수신을 보장하지는 않는다. + +하나의 사건에서 뒤쪽 반응이 실패하면 앞쪽 상태 변경도 확정하지 않는다. +이는 전체 드래그를 자동 되돌리는 것과 다르다. 전체 복귀는 작성한 cancel 반응이다. +취소 반응 자체가 실패하더라도 호스트의 입력 소유권은 해제하고 오류를 보존한다. +CLI의 `point NAME PHASE X Y`도 같은 호스트 API와 코어를 사용한다. + +## 검증 목적과 결과 + +검증 질문은 **작성한 직접 조작이 입력의 시작·변경·종료·취소에서 일관되는가**다. +처리량이나 실행 시간을 연구 결과로 제시하지 않는다. 테스트 도구가 자동 출력하는 +실행 시간도 성능 증거로 해석하지 않는다. + +- 네이티브 26개: 좌표 문맥, 유한 값과 사건 범위, 표시 계산에서 좌표 접근 거부, + 여러 반응에 걸친 실패 시 원자성 및 기존 코어 동작. +- JavaScript 59개: 컴파일러·런타임·출력·CLI 회귀, 입력 소유권, 중복/오래된 사건, + 마지막 놓기 좌표, 좌표로 변경한 상태의 출력 갱신, CLI 재생 동등성. +- 브라우저 직접 조작 26개: 실제 마우스로 장면 밖까지 끌기와 놓기, + 캡처 해제, 초점 상실과 취소, 작성 코드의 제한 변경, 실패 후 정리, + 축소 화면의 터치와 서로 다른 대상의 두 접촉, 동일 대상의 중복 접촉. + 페이지 오류는 없었다. +- 기존 작성 흐름 브라우저 44개: 빈 문서 시작, 편집·적용·초기화, 키 입력, + 파일 저장·불러오기, 오류 표시와 모바일 레이아웃의 회귀 검증. + +브라우저는 Linux AArch64 Chromium이다. 터치는 Chromium CDP 입력을 사용했다. +초점 상실과 단일 포인터 cancel은 사건 주입, 캡처 상실은 실제 캡처 해제 API, +마우스 조작은 Playwright 마우스 입력으로 확인했다. +실제 휴대폰·Safari·펜 압력·기울기 동작을 검증했다고 주장하지 않는다. + +근거: [검증 로그](../../prototypes/workbench/results/2026-09-23-pointer/verification.json), +[브라우저 검사](../../prototypes/workbench/results/2026-09-23-pointer/pointer-browser.json), +[작성 흐름 회귀](../../prototypes/workbench/results/2026-09-23-pointer/authoring-browser.json), +[재현 스크립트](../../prototypes/workbench/qa-pointer.js), +[상세 계약](../../prototypes/workbench/CONTRACT.md). + +## 이번 결과의 의미와 다음 작업 + +새 제스처 이론이나 대규모 성능 우위를 입증한 결과는 아니다. +화면의 대상에 좌표 입력을 연결하고, 작성 코드가 동작을 정하며, +기존 상태 확정과 출력 경로로 이어지는 기능을 확보했다. +별도 호스트 이동 상태 없이 이 경로가 동작하는 것이 이번 단계의 결과다. + +현재 상태 32개·입력 8개·출력 24개라는 한도는 그대로다. +실제 컨테이너 소속 변경, 다중 선택, 확대·축소, 배열·가변 개체 집합은 구현하지 않았다. +두 배경 영역을 가로지르는 예제는 컨테이너 간 재배치 구현이 아니다. +저장 기능도 현재는 소스 저장이며 조작 후 배치 상태의 저장·복원은 아니다. + +다음은 같은 작업물을 **여러 대상을 배치하고 결과를 저장·복원하는 도구**로 연결한다. +작업 중 발생하는 상태·정의·파일의 일관성과 현재 고정 개수 모델의 제약을 확인한다. +규모를 억지로 키운 숫자 대신, 실제 작업을 막는 제약이 확인될 때 구조 변경과 측정을 택한다. + + +## 사용자 피드백에 따른 놓기 수정 + +Codex 내장 브라우저에서 놓으면 되돌아가며 마지막 사건이 up 또는 cancel로 표시된다는 +보고가 있었다. 기존 Chromium 검사가 내장 브라우저의 정상 동작까지 입증하지는 않는다. +놓기가 SVG 밖에 전달된 뒤 캡처를 잃는 순서를 주입하면 기존 코드에서 cancel과 +원위치 복귀가 재현됐다. 내장 브라우저의 실제 사건 순서를 직접 확보한 것은 아니다. + +놓기·취소를 창의 캡처 단계에서 받고, pointerup 없이 전달된 기본 mouseup도 활성 +마우스 입력에 한해 처리한다. 종료 후 늦은 캡처 해제는 중복 종료를 만들지 않는다. +예제의 up은 위치를 다시 계산하지 않고 마지막으로 표시된 위치를 확정하도록 변경했다. +놓기 좌표가 누르기 좌표를 반복하더라도 이동 결과가 튀지 않는다. 좌표가 필요한 작성자는 +여전히 up에서 event_x/event_y를 읽을 수 있다. 실제 cancel/초점 상실의 복귀는 유지한다. +이 수정의 재현 검사는 [qa-release.js](../../prototypes/workbench/qa-release.js)에 있다. diff --git a/docs/research/014-layout-persistence.md b/docs/research/014-layout-persistence.md new file mode 100644 index 0000000..5746552 --- /dev/null +++ b/docs/research/014-layout-persistence.md @@ -0,0 +1,93 @@ +# 014 — 여러 대상을 배치하고 작업을 다시 열기 + +2026-09-23. 이번 단계는 작성한 인터페이스를 한 번 조작하고 끝내지 않고, +작업 결과를 저장한 뒤 새 실행기에서 계속 편집할 수 있게 하는 것이다. +처리량·지연·대규모 합성 벤치마크는 수행하지 않았다. + +## 실제로 할 수 있는 일 + +[세 도형 배치](../../prototypes/workbench/examples/layout.pure)를 불러와 적용하면 +각 대상을 따로 움직일 수 있다. 마지막으로 잡은 대상은 방향키로 조정한다. +**배치 저장** 후 파일을 **파일 열기**로 다시 열면 적용된 코드와 현재 배치가 복원된다. +아직 적용하지 않은 미완성 코드도 별도 초안으로 남는다. +새 페이지 또는 CLI에서 열어도 기존 실행기의 값을 참조하지 않는다. + +색상·이동·선택·키 반응은 예제 소스에 있다. 호스트에는 세 도형 전용 상태나 +이동 코드를 추가하지 않았다. 이전 드래그 놓기 수정도 유지했다. +기존 .pure 저장과 코드 JSON 저장은 계속 초안만 저장하므로 배치 저장과 구별된다. + +## 무엇이 일치해야 하는가 + +한 작업물에 서로 다른 세 종류의 정보가 있다. + +| 정보 | 저장·복원 의미 | +|---|---| +| 적용된 코드 | 복원한 값을 해석하고 실행할 정의 | +| 현재 작성 상태 | 대상 위치·선택 등 선언된 모든 숫자 값 | +| 편집 초안 | 미적용·문법 미완성이어도 보존, 자동 실행하지 않음 | + +현재 값을 코드의 초깃값으로 덮어쓰면 원래의 초기화 의미가 사라진다. +반대로 코드만 저장하면 조작 결과가 사라진다. 따라서 소스와 현재 값을 별도로 저장하고, +복원 시 한 번에 확정한다. **초깃값으로**는 여전히 작성한 소스의 초기값으로 돌아간다. + +입력 소유권·시계·오류 기록·계산 캐시·렌더러 객체는 작업 결과에 포함하지 않는다. +복원은 정지 상태에서 시작하며 입력을 재생하지 않는다. 저장 전에 조작과 시간 진행을 +끝내도록 한다. 실행 실패가 남아 있으면 해결한 뒤 저장한다. +작성자가 상태로 선언한 드래그 보조 값은 현재 모두 저장한다. 영속·일시 상태를 +구분하는 언어 기능이 생겼다고 주장하지 않는다. + +## 복원 구현 + +version2 파일은 적용된 소스, 별도 초안, 이름과 값의 목록을 가진다. +모든 상태 이름이 정확히 한 번씩 있어야 하며, 값은 유한한 숫자여야 한다. +순서는 의미가 없다. 음의 0은 JSON 숫자 직렬화에서 사라지므로 전용 문자열 `"-0"`로 +보존한다. 다른 숫자 문자열은 허용하지 않는다. 파일 크기와 코드 한도를 검사한다. +version1은 기존 코드 전용 파일로 계속 지원한다. + +호스트는 정의와 상태 목록을 검사한 뒤 코어의 준비 공간에 넣는다. +코어는 전체 정의와 모든 복원 값을 검증한 후 활성 정의·상태를 함께 교체한다. +값 하나를 넣다 실패해 현재 배치가 절반만 바뀌는 경로가 없다. +새 준비 값은 기존 로드/계산 기록 공유 공간에 들어가며, 활성 프로그램 형식은 늘리지 않았다. + +32개 복원 값과 유효성 비트·정렬 때문에 준비 공간은 264 B 늘었다. +현재 Wasm 코어 예약 구조체는 19,392 B, 선형 메모리 한도는 여전히 64 KiB다. +이는 원자적 복원에 사용한 저장 공간의 회계이며 성능 개선 주장이나 프로세스 RAM 측정이 아니다. + +브라우저의 비동기 파일 읽기에는 초안 변경 번호, 적용 정의의 동일성, 성공 사건 수를 확인한다. +읽는 도중 편집하거나 도형을 움직였다면 파일을 다시 열도록 안내한다. +느린 파일 읽기가 뒤늦게 현재 작업을 덮어쓰지 않는다. +CLI `save-project`와 `restore`도 같은 검증·복원 API를 사용하고 기존 파일은 덮어쓰지 않는다. + +## 검증 + +- 네이티브 29개: 정의·복원 값의 원자적 확정, 부족·중복·비유한 값 거절, + 0개·32개 상태 경계, 기존 활성 실행과 준비 값의 분리, 원래 초기값 유지. +- JavaScript 68개: 소스·상태·미완성 초안의 왕복, 상태 순서 변경, 음의 0, + 잘못된 파일의 비변경, 캐시 무효화, 조작 중 거절, CLI 파일 왕복과 기존 동작. +- 실제 브라우저 작성·저장 17개: 세 대상 이동, 키보드 조정, 다운로드 파일로 + 새 페이지 복원, 잘못된 파일, 편집·입력과 비동기 읽기의 충돌, 조작 중 저장 거절, + 모바일에서 추가 버튼의 가로 넘침 여부. +- 기존 브라우저 작성 흐름 44개: 코드 저장·불러오기, 편집·적용·오류·키 입력 등 회귀 검사. + +브라우저 검증은 Linux AArch64 Chromium에서 수행했다. Codex 내장 브라우저에서 +이 새 파일 흐름까지 사용자가 검증한 것은 아니다. 앞선 놓기 수정은 사용자가 정상 동작을 확인했다. +테스트 실행 도구의 경과 시간은 성능 근거로 해석하지 않는다. + +근거: [검증 로그](../../prototypes/workbench/results/2026-09-23-layout/verification.json), +[코어 빌드·테스트](../../prototypes/workbench/results/2026-09-23-layout/build.json), +[브라우저 결과](../../prototypes/workbench/results/2026-09-23-layout/layout-browser.json), +[기존 작성 흐름](../../prototypes/workbench/results/2026-09-23-layout/authoring-browser.json), +[브라우저 재현](../../prototypes/workbench/qa-layout.js), +[문서 계약](../../prototypes/workbench/CONTRACT.md). + +## 다음에 풀 제약 + +세 도형을 만들면서 대상마다 위치·잡은 위치·취소 위치·입력·반응을 반복 선언해야 했다. +같은 이동 규칙이 대상 수만큼 커지는 구조가 현재의 구체적인 제약이다. +현재 예제만으로 대규모 도구의 적합성이나 개발 경험 우위를 주장하지 않는다. + +다음 후보는 **대상 집합의 데이터는 분리하고, 같은 반응은 한 번만 정의하는 실행 표현**이다. +포인터가 소유한 대상, 키보드가 선택한 대상, 저장한 대상의 식별이 일치해야 한다. +개체 추가·삭제 뒤 잘못된 대상에 입력이 적용되지 않는 조건도 함께 검토해야 한다. +문법을 꾸미거나 기존 수치를 반복 측정하기보다 이 반복과 식별 문제를 먼저 해결한다. +현재 파일 저장은 수동 저장이며 자동 저장·실행 취소·다중 사용자 동기화는 구현하지 않았다. diff --git a/docs/research/015-shared-rules.md b/docs/research/015-shared-rules.md new file mode 100644 index 0000000..44f6544 --- /dev/null +++ b/docs/research/015-shared-rules.md @@ -0,0 +1,115 @@ +# 015 — 대상별 값과 공유 규칙 본문 + +2026-09-23. 여러 도형을 배치하는 작업에서 같은 이동·놓기·취소 반응이 대상마다 +복제되던 문제를 다뤘다. 규칙을 한 번 작성하고, 컴파일된 명령도 한 벌만 보관하며, +각 입력 연결은 대상의 연속된 상태 구간을 지정한다. + +## 사용하는 형태 + +```text +record Position { x=0; y=0 } +item green = Position(x=80,y=120) +item blue = Position(x=280,y=160) +rule move(t: Position) { + t.x = event_x + t.y = event_y +} +on drag_green.move use move(green) +on drag_blue.move use move(blue) +``` + +전체 작성물 [shared-layout.pure](../../prototypes/workbench/examples/shared-layout.pure)는 +잡은 위치 유지, 제한된 영역의 이동, 놓기 확정, 취소 복귀, 선택한 대상의 방향키 조정을 포함한다. +화면의 **공유 규칙으로 배치**에서 불러와 적용할 수 있다. +`go_right` 한 곳을 고치면 연결된 세 대상이 같은 변경을 사용한다. +대상별 입력과 출력 연결은 명시적으로 작성한다. + +## 실행 표현 + +`record`는 필드의 기본값, `item`은 그 필드의 실제 값, `rule`은 공유되는 반응이다. +각 item의 필드는 기존 숫자 상태 배열의 연속 구간에 놓인다. `green.x` 같은 이름은 +작성·수정·저장의 식별자이며, 이벤트마다 문자열을 찾아 실행하지 않는다. + +규칙 본문은 첫 사용 위치에서 한 번만 내린다. 연결마다 본문 범위와 대상 필드의 +시작 위치·개수를 기록한다. 읽기·쓰기는 `base + field_offset`으로 실제 상태에 접근한다. +필드 번호 목록, 인자 값 복사, 별도 레코드 객체, VM 호출 스택을 추가하지 않았다. +표시 표현식은 직접 해석된 기존 상태 읽기를 쓰므로 계산 캐시도 실제 필드 변경을 추적한다. + +매개변수 하나는 정확히 같은 record 타입의 item에 연결한다. 다른 타입, 누락된 필드, +범위를 벗어난 슬롯, 잘못된 본문 공유는 거절한다. 사용하지 않은 규칙도 이름과 필드를 +검사하지만 명령 저장량에는 포함하지 않는다. 매개변수 이름이 전역 item 이름과 겹칠 때 +없는 필드를 전역 값으로 잘못 해석하지 않도록 검사한다. + +한 사건의 모든 연결은 기존처럼 작성 순서대로 실행한다. 뒤쪽 연결이 실패하면 앞쪽 +연결의 변경과 전역 상태 변경까지 함께 취소한다. 같은 item을 두 번 연결하면 두 번 실행한다. +코드를 공유한다는 이유로 입력이나 효과를 생략하지 않는다. + +## 확인한 구조적 차이 + +동일한 최종 코어에서 기존 `layout.pure`와 공유 규칙 작성물을 컴파일한 회계다. +실행 시간 비교나 처리량 벤치마크는 아니다. + +| 항목 | 기존 명시적 반복 | 공유 규칙 | +|---|---:|---:| +| 저장된 명령 | 319 | 117 | +| 이벤트 연결 | 16 | 24 | +| 사용 중인 명령·연결 자료 | 5,360 B | 2,256 B | +| 숫자 상태 | 22 | 25 | + +공유 버전의 추가 세 상태는 예제가 명시한 대상별 `id`다. 기존 키 반응의 대상별 조건을 +각 연결에 나누면서 연결 수는 늘었다. 코드 저장량 감소를 전체 메모리 감소나 속도 향상으로 +해석하면 안 된다. 각 대상의 값과 입력 소유권은 계속 필요하다. + +연결 구조체는 12 B에서 16 B로 늘었다. 활성·준비 프로그램의 고정 연결 배열 때문에 +코어 예약 공간은 이전 19,392 B에서 **19,904 B**로 512 B 늘었다. +Wasm 선형 메모리 한도는 64 KiB다. 사용 중인 자료와 고정 예약 공간을 별도로 기록한다. +이 구조가 모든 작은 작성물에 메모리 이득이라는 주장은 하지 않는다. +[구조 회계](../../prototypes/workbench/results/2026-09-23-shared-rules/representation.json)는 +동일 ABI의 두 작성물을 비교하며 이전 12 B 연결 구조체의 수치를 섞지 않는다. + +## 의미 보존과 검증 + +기존 작성물과 공유 버전에 같은 입력을 전달해 모든 대상의 위치·잡은 위치·취소 위치· +조작 여부·선택·출력 값을 비교했다. 두 대상의 동시 조작, 한 대상의 취소, 키보드 조정, +영역 밖 이동과 놓기 이후의 결과가 일치했다. + +선언 순서와 record 필드 순서를 바꿔도 `item.field` 이름으로 값을 보존하고 물리 연결을 +다시 만든다. 삭제한 item의 값은 사라지며 재선언은 새로운 초기값에서 시작한다. +배치 파일은 물리 슬롯 번호를 저장하지 않는다. 새 실행기에서 소스를 다시 컴파일하고 +이름을 검사한 다음 정의와 값을 함께 확정한다. 이전 version2 형식은 유지한다. +생성되는 필드 이름이 소스보다 길어질 수 있어, 최대 크기의 유효한 저장 파일을 자체 +로더가 거절하지 않도록 파일 크기 상한도 해당 이름 수를 포함해 정했다. + +- 네이티브 32개: 공유 명령의 독립 필드 접근, 같은 대상의 반복 연결, 여러 연결의 + 원자성, 누락·잘못된 범위·부분 중첩 거절과 기존 코어 기능. +- JavaScript 75개: 규칙 하나의 명령 수가 연결 증가로 복제되지 않음, 컴파일 오류, + 이전 작업물과 의미 동등성, 순서 변경·제거·복원, 캐시 무효화, 기존 작성·파일 기능. +- 브라우저 23개: 공유 규칙 한 곳 수정, 서로 다른 대상에 적용, 선언 순서 변경, + 실제 마우스와 두 접촉의 독립 이동·취소, 다운로드와 새 페이지 복원, 파일 읽기 충돌. + +기존 작성 흐름의 브라우저 회귀 검사 44개도 통과했다. + +브라우저는 Linux Chromium이며 터치는 CDP 입력이다. 실제 모바일 하드웨어나 Codex 내장 +브라우저에서 이번 공유 규칙 흐름까지 사용자가 확인한 것은 아니다. + +근거: [Node 검증](../../prototypes/workbench/results/2026-09-23-shared-rules/verification.json), +[코어 빌드](../../prototypes/workbench/results/2026-09-23-shared-rules/build.json), +[브라우저 결과](../../prototypes/workbench/results/2026-09-23-shared-rules/shared-browser.json), +[재현 스크립트](../../prototypes/workbench/qa-shared-rules.js), +[계약](../../prototypes/workbench/CONTRACT.md). + +## 남은 제약과 다음 방향 + +매개변수화된 규칙은 기존 프로그래밍 개념이다. 이번 결과는 이 작성·실행·저장 경로에서 +실제 명령 복제를 없애면서 상태와 조작 의미를 보존한 구현이다. +현재 record당 필드는 1~8개이고, 모든 item의 필드는 합쳐서 상태 32개 한도에 포함된다. +입력 8개·출력 24개 한도도 그대로다. 일반 함수 호출, 동적 대상 생성·삭제는 없다. + +이름으로 보존하는 정적 필드와 동적 대상의 생명주기 핸들은 다르다. +예제의 `id`와 `selected`는 작성한 숫자 상태라서 중복 방지나 선택 정책을 실행기가 +자동 보장하지 않는다. 재사용한 ID를 오래된 입력이 가리키는 문제까지 해결한 것으로 세지 않는다. + +다음 제약은 대상마다 필요한 입력 연결과 키 사건의 모든 연결 순회다. +**실행 중 대상 추가·삭제와 선택한 대상만 실행하는 경로**를 검토할 때, +삭제·재사용 뒤 오래된 입력과 저장된 선택이 잘못된 대상에 적용되지 않는 식별 규칙이 필요하다. +규모를 키운 속도 수치보다 이 작업 조건을 먼저 고정한다. diff --git a/docs/research/016-pointer-routing.md b/docs/research/016-pointer-routing.md new file mode 100644 index 0000000..933e505 --- /dev/null +++ b/docs/research/016-pointer-routing.md @@ -0,0 +1,98 @@ +# 016 — 대상마다 복제하던 포인터 연결 제거 + +2026-09-23. 앞 단계는 규칙 본문을 공유했지만, 세 도형의 드래그를 위해 입력 세 개와 +반응 연결 열두 개를 따로 만들었다. 이번에는 **입력 하나와 반응 네 개에 대상 표를 연결**한다. +현재 잡은 도형의 상태 구간을 규칙에 전달하고, 접촉 소유권은 도형별로 유지한다. + +## 작성과 사용 + +```text +input drag = pointer { + green_box: green + blue_box: blue + amber_box: amber +} +on drag.down use grab(drag.target) +on drag.move use move(drag.target) +on drag.up use drop(drag.target) +on drag.cancel use cancel(drag.target) +``` + +작업 화면을 새로고침하고 **입력 묶어서 배치 → 예제 불러오기 → 코드 적용**을 누른다. +[전체 작성물](../../prototypes/workbench/examples/routed-layout.pure)은 이전과 같은 +잡은 위치 유지, 경계 제한, 놓기 확정, 취소 복귀, 선택한 도형의 방향키 조정을 제공한다. +한 그룹 안의 도형 두 개를 동시에 움직일 수 있다. 하나를 놓거나 취소해도 다른 도형의 +소유권은 유지되며, 이미 놓은 도형은 다른 접촉을 취소해도 되돌아가지 않는다. + +같은 record 타입의 item끼리 묶는다. 도형과 대상의 연결은 명시하지만 네 반응은 +대상마다 작성하거나 컴파일해 복제하지 않는다. 마지막 입력 표시에는 대상 도형도 나온다. +CLI는 `point-target drag green_box move 210 220`으로 같은 경로를 실행한다. + +## 실제 실행 표현 + +컴파일러는 도형 이름을 item의 연속 필드 구간에 연결한다. VM에 저장되는 대상 하나는 +`input, base, count` 세 바이트다. 그룹 반응의 메타데이터에는 ‘이번 사건의 대상’을 +사용한다는 표시와 필드 수만 둔다. 명령 본문과 연결 수는 도형 수만큼 늘어나지 않는다. + +브라우저는 처음 잡은 도형을 캡처 기록에 유지한다. 런타임은 코드 적용 시 만든 이름 +조회 표로 대상 번호를 찾고, VM은 그 번호로 활성 대상 표를 직접 읽는다. +포인터가 움직일 때마다 그룹의 모든 도형을 검색하거나 각 도형의 규칙을 실행하지 않는다. +기존의 제한된 핸들러 목록에서 사건 번호를 비교하는 순회는 남아 있다. + +소유권 키는 입력 이름과 도형 이름의 조합이다. 같은 도형의 두 번째 접촉은 소유권을 +가져갈 수 없다. `releaseAll`은 잡고 있는 도형 각각에 취소를 한 번씩 보낸다. +그룹끼리 같은 item을 중복 연결하는 것도 거절해 필드 구간이 겹치는 캡처를 막는다. +작성자가 다른 전역 상태나 명시적 대상에 쓰는 일반 반응까지 금지하는 정책은 아니다. + +VM은 잘못된 대상 번호, 다른 입력의 대상, 누락된 대상, 범위를 벗어난 필드를 거절한다. +하나의 사건에서 앞의 반응이 쓴 값도 뒤의 반응이 실패하면 함께 취소한다. +준비 중인 연결 표는 활성 표를 바꾸지 않는다. 소스 적용과 파일 복원은 입력을 모두 +놓은 뒤에만 가능하고, 이름을 기준으로 표를 다시 만든다. 파일에는 물리 대상 번호를 저장하지 않는다. + +## 확인한 비용과 한계 + +동일한 최종 코어에 두 작성물을 넣은 저장 구조 회계다. 속도 벤치마크는 하지 않았다. + +| 항목 | 공유 규칙 + 대상별 입력 | 공유 규칙 + 그룹 입력 | +|---|---:|---:| +| 전체 입력 | 7 | 5 | +| 포인터 반응 연결 | 12 | 4 | +| 전체 반응 연결 | 24 | 16 | +| 저장 명령 | 117 | 117 | +| 대상 표 | 0 B | 9 B | +| 사용 중인 명령·연결·대상 표 | 2,256 B | 2,137 B | +| 숫자 상태 | 25 | 25 | + +대상 표 24칸을 예약하면서 활성/준비 프로그램에 각각 80 B가 추가됐다. +코어 고정 예약은 **19,904 → 20,064 B**, Wasm 선형 메모리는 64 KiB로 유지된다. +현재 예제의 사용 자료는 줄었지만, 고정 예약은 늘었다. JS의 조회 Map과 메타데이터, +브라우저 전체 메모리는 이 수치에 포함하지 않는다. 전체 메모리나 실행 속도의 우위로 해석하지 않는다. +[구조 회계](../../prototypes/workbench/results/2026-09-23-pointer-routing/representation.json). + +1·3·9·24개의 작은 대상을 컴파일해 하나의 입력과 하나의 이동 반응이 유지되는지도 검사했다. +이는 기존 입력 8개 한도와 대상 수를 분리했는지 확인하는 경계 검사다. +큰 앱이나 많은 도형의 성능 실험으로 세지 않는다. 총 상태 32개, 출력 24개 등의 +기존 한도는 그대로이며, 현재 Tile의 8필드 구성으로 24개 도형을 만들 수 있다는 뜻도 아니다. + +## 검증과 남은 작업 + +코어 검사 35개와 Node/CLI 검사 83개가 통과했다. 기존 배치와 동일한 입력을 전달해 +모든 필드와 출력을 비교했고, 동시 접촉·일부 취소·소유권 침범 거절·그룹 혼동 거절· +실패 원자성·표시 캐시 갱신·선언 순서 변경·제거 후 재선언·파일 복원을 확인했다. + +그룹 예제의 브라우저 확인 30개, 기존 작성 흐름 44개, 이전 대상별 포인터 흐름 +23개도 통과했다. +브라우저에서는 실제 마우스와 Chromium CDP 터치로 조작한다. 개별 접촉 종료 검사에서 +처음에 남겨둘 접촉을 종료하도록 보낸 오류를 바로잡았다. 기록된 실제 `pointerup`과 +남은 소유권을 함께 확인했다. 이동 검사는 CDP 명령 응답 이후 실제 사건이 +반영될 때까지 기다린다. 이 재현은 실제 모바일 하드웨어 검증을 대신하지 않는다. +[검증 자료와 재현](../../prototypes/workbench/results/2026-09-23-pointer-routing/README.md). + +이 결과는 입력마다 대상을 별도 연결하던 비용을 분리한 구현이다. 독창적인 새 +프로그래밍 개념이나 대규모 서비스 최적화의 증거로 제시하지 않는다. + +다음 문제는 **선택 대상의 식별과 키 입력의 직접 전달**이다. 현재 예제의 `selected`와 +`id`는 작성한 숫자 상태이며, 방향키는 여전히 대상별 조건을 검사한다. 이를 해결할 때는 +선택 없음, 선택 대상 제거, 저장 복원 뒤 선택, 잡고 있는 대상과 선택이 다를 때의 정책을 +먼저 고정해야 한다. 동적 생성·삭제에는 오래된 입력이 재사용된 슬롯으로 전달되지 않는 +수명 식별도 필요하다. 이번 대상 번호는 정적 정의 안의 주소이며 그 역할을 대신하지 않는다. diff --git a/docs/research/017-web-layout-boundary.md b/docs/research/017-web-layout-boundary.md new file mode 100644 index 0000000..042c969 --- /dev/null +++ b/docs/research/017-web-layout-boundary.md @@ -0,0 +1,116 @@ +# 017 — 웹 레이아웃을 복제하지 않는 문서 작업 + +**범위 정정 — 2026-09-23:** 아래 결과는 브라우저 호스트에 한정한다. Pure의 목표는 앱 로직· +net·RPC·DB·UI를 다루는 독립 네이티브 애플리케이션 런타임이다. UI는 일부다. +웹 호환성을 브라우저에 레이아웃·편집을 맡기는 것으로 해석한 것은 +Codex의 오류다. 이 실험의 기능 검증은 보존하지만, Pure 자체의 레이아웃·텍스트·입력 체계가 +불필요하다는 근거로 사용하지 않는다. 현재 구현 방향은 [010 로드맵](010-roadmap.md)을 따른다. + +2026-09-23. 일반 텍스트를 열고 편집·읽기·저장하는 화면에 Pure를 연결했다. +**이 작업에서는 레이아웃 트리·텍스트·스크롤 위치를 VM으로 옮길 필요가 없었다.** +크기 변경과 줄바꿈은 브라우저가 처리하고, Pure에는 사용자가 정한 패널 비율 하나만 둔다. + +이것은 실제 UI 조건을 확인한 구현·기능 검증이다. 전체 메모리 절감, 새로운 압축 원리, +대규모 앱 성능 우위의 증거는 아니다. 화면 하나의 분할 비율을 위해 VM을 추가할 성능상의 +근거도 얻지 않았다. 기존 실행 규칙을 표준 웹 호스트에 연결하는 경계를 확인한 단계다. + +## 직접 확인하기 + +기존 작업 화면을 새로고침하고 **문서 편집 · 반응형 레이아웃 작업 화면** 링크를 연다. +직접 주소는 서버의 `/document.html`이다. + +1. 일반 UTF-8 `.txt` 파일을 열거나 빈 문서에 작성한다. **예제 문서**도 있다. +2. 가운데 경계를 끌거나 방향키·슬라이더로 두 패널의 폭을 조절한다. +3. 조작 중 창 크기를 바꾸거나 Esc로 취소한다. 좁은 화면에서는 두 패널이 위아래로 놓인다. +4. 양쪽을 독립적으로 스크롤하고, 텍스트를 고치고 실행 취소한다. +5. `.txt 저장`으로 다른 편집기에서도 읽을 수 있는 파일을 내려받는다. +6. **분할 규칙 수정**에서 `clamp(event_value,20,80)`을 `clamp(event_value,30,70)`으로 + 바꾸어 적용한다. HTML 어댑터를 바꾸지 않아도 같은 컨트롤의 제한이 바뀐다. + +파일은 텍스트만 저장한다. 패널 비율·Pure 규칙을 묶는 자체 문서 형식은 만들지 않았다. +임포트/익스포트는 파일당 2 MiB까지이고, UTF-8 오류는 현재 문서를 유지한 채 거절한다. +브라우저 textarea의 줄바꿈 정규화 때문에 저장은 LF·BOM 없음으로 이루어진다. +원본 파일의 모든 바이트를 그대로 보존하는 편집기라는 뜻은 아니다. + +## 어디에서 무엇을 소유하는가 + +| 자료/작업 | 소유자 | 연결에서 하는 일 | +|---|---|---| +| 작성 중인 텍스트·선택·실행 취소 | 브라우저 기본 textarea | 입력 후 읽기 화면에 텍스트 전달 | +| 읽기 화면의 글자 배치·줄바꿈·스크롤 | DOM/CSS/브라우저 | 문서가 바뀔 때만 텍스트 갱신 | +| 패널의 실제 크기·좁은 화면 배치 | CSS Grid/media query | 비율 하나를 CSS 변수로 전달 | +| 사용자가 요청한 분할 비율의 정책 | Pure 숫자 상태·규칙 | range/포인터/키보드가 같은 숫자 입력을 사용 | +| 접촉 소유권·취소 시작값·기하 캐시 | 브라우저 어댑터 | 조작 시작과 무효화 뒤 필요한 좌표만 읽음 | + +문서 크기나 줄 수가 늘어나도 이번 정책의 VM 상태는 비율 하나다. viewport width, +DOMRect 배열, 텍스트 AST, 스크롤 위치를 VM 상태로 복제하지 않는다. 레이아웃 연산이 +없어진 것은 아니다. 브라우저가 그 연산과 관련 저장 공간을 담당한다. + +읽기 화면은 textarea와 별도의 텍스트 표현을 갖는다. 따라서 **제로 카피 문서 모델은 아니다.** +별도 JS 문서 모델과 Wasm 문자열 사본은 만들지 않았지만, 브라우저 내부의 텍스트·undo· +레이아웃 저장 공간과 파일 읽기/인코딩의 임시 버퍼는 여전히 필요하다. + +## 필요한 경계만 추가했다 + +기존 실행기는 키·포인터·프레임 입력은 받았지만 네이티브 숫자 컨트롤의 값을 직접 받을 +경로가 없었다. 다음 입력을 추가했다. + +```text +state ratio = 50 +input split = value +on split.change { ratio = clamp(event_value,20,80) } +``` + +호스트는 `runtime.change('split',65,'range')`, CLI는 `change split 65`를 사용한다. +값은 유한한 숫자이고 사건 동안만 존재한다. 규칙이 저장한 값만 상태가 된다. +기존 원자적 이벤트 실행·실패 취소·공유 규칙·표시 캐시 무효화를 그대로 사용한다. +문서 호스트는 HTML로 출력하므로 가짜 도형 선언이나 SVG 장면을 만들지 않는다. +출력 도형이 없는 프로그램에서는 사용하지 않는 표시값 배열도 생성하지 않도록 했다. + +[정의](../../prototypes/workbench/examples/document.pure)의 실제 표현은 상태 1개, +입력 2개, 반응 2개, 명령 7개다. 활성 상태는 8 B, 명령·연결은 144 B다. +하지만 코어는 여전히 **20,064 B를 예약하고 Wasm 선형 메모리는 64 KiB**다. +이 숫자는 브라우저 앱 전체 메모리가 아니며, 작은 상태만으로 전체 효율을 주장하지 않는다. + +포인터 이동은 프레임당 마지막 좌표만 전달한다. 놓을 때 대기 중인 이동을 먼저 처리하고, +취소는 시작 비율로 복귀한다. 크기 변경·스크롤은 기하 캐시를 무효화하지만 그 콜백에서 +레이아웃을 읽거나 쓰지 않는다. 다음 조작이 실제로 필요할 때 읽는다. +관찰된 0회는 애플리케이션의 해당 API 호출 수이며 브라우저 내부 레이아웃 비용 0을 뜻하지 않는다. + +## 확인한 것과 한계 + +- 네이티브 37개, Node/CLI 87개 검사 통과. 새 숫자 사건의 컨텍스트, 잘못된 입력, + 비유한 수, 여러 반응의 실패 원자성, 공유 규칙, 캐시, 저장·복원을 포함한다. +- 문서 브라우저 검사 32개: 창 크기 변경·독립 스크롤·네이티브 컨트롤·실제 마우스와 + CDP 터치·조작 중 resize·취소·표준 파일 다운로드와 새 페이지 왕복·늦게 끝난 파일 읽기 + 거절·Unicode 편집·네이티브 undo·미완성 조합 중 출력 보류·규칙 수정·Wasm 실패 시 문서 기능 유지. +- 기존 작성 흐름 44개도 통과했다. 기본 화면의 숫자 입력 컨트롤 3개, 잘못된 출력·조작 중 교체·파일 크기 등의 + 추가 확인 6개도 통과했다. +- 창 크기 변경과 스크롤에서 Pure 이벤트·문서 재전달·앱 기하 읽기가 증가하지 않음을 + 확인했다. 비율 변경도 문서 재전달을 일으키지 않는다. 이는 소유권 검증이며 속도 비교가 아니다. + +환경은 Linux Chromium이다. 한글 조합의 순서는 합성 composition 사건으로 확인했고, +실제 운영체제 IME·물리 모바일 기기·전체 접근성 적합성을 검증한 것은 아니다. +분할 조작은 [W3C의 separator 의미와 키보드 지침](https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/)을 +참고했다. 포인터 캡처·취소는 [Pointer Events](https://www.w3.org/TR/pointerevents3/), +크기 관찰 경계는 [Resize Observer](https://drafts.csswg.org/resize-observer/)를 참고했다. +표준을 재발명한 것으로 세지 않는다. + +[검증 자료와 재현](../../prototypes/workbench/results/2026-09-23-web-layout/README.md), +[실행 계약](../../prototypes/workbench/CONTRACT.md), +[문서 호스트](../../prototypes/workbench/web/document.mjs), +[입력 어댑터](../../prototypes/workbench/web/split-input.mjs). + +## 당시 결정의 정정과 남은 한계 + +당시 ‘기존 환경과의 경계가 이미 목적을 충족하므로 자체 레이아웃을 추가하지 않는다’는 +결론은 철회한다. 브라우저를 필수 기반으로 삼은 이 구현은 독립 네이티브 UI 목표를 충족하지 +않는다. 현재 HTML 화면은 직접 작성한 호스트이며 현재 DSL이 일반 UI 전체를 표현하지도 않는다. +Pure의 UI 체계가 배치·입력·출력을 소유하는 일은 애플리케이션 런타임 구현의 일부다. +다음 우선순위는 로드맵의 실행·수명 계약과 UI·RPC·DB를 연결하는 실제 작업을 따른다. +이는 모든 기능을 현재 VM에 넣거나 새 문서·미디어 포맷을 먼저 만들겠다는 뜻은 아니다. + +남아 있는 구체적 비용은 **텍스트 한 부분을 편집해도 읽기 화면의 전체 문자열을 갱신하는 것**이다. +좁은 화면에서 읽기 패널이 보이지 않을 때도 지금은 갱신한다. 이 웹 호스트의 추가 최적화는 +미룬다. 표준 파일 왕복·숫자 입력·취소 등의 동작은 네이티브 구현에서 참고하되, DOM/CSS에 +맡긴 비용이 제거됐다고 계산하지 않는다. 표준 파일을 포기하거나 새 포맷의 우위를 가정하지 않는다. diff --git a/docs/research/018-native-connections.md b/docs/research/018-native-connections.md new file mode 100644 index 0000000..f2c26bc --- /dev/null +++ b/docs/research/018-native-connections.md @@ -0,0 +1,81 @@ +# 018 — 요청·DB 결과·전송의 수명을 연결한 네이티브 경로 + +> 역사적 기록: 이 문서의 SQLite 구현은 사용자 요구와 맞지 않아 +> [019의 SQL 없는 바이트 저장](019-byte-storage.md)으로 교체했다. 아래 결과와 원자료는 +> 당시 구현의 기록이며 현재 구현의 의존성·검증 결과를 뜻하지 않는다. + +2026-09-23. **기존 작성물의 저장·복원을 실제 TCP와 SQLite에 연결했다.** 한 실행기에서 +이동한 배치를 저장하고 다른 실행기에서 복원할 수 있다. 새 네이티브 서버는 브라우저 없이 +실행되며, 기존 CLI가 요청한다. 이번 결과는 실제 연결의 구현·정확성 확인이다. 속도·전체 +메모리 우위나 새 알고리즘의 증거는 아니다. + +알아낸 것은 연결의 책임 범위다. DB 결과를 응답 본문 배열로 다시 복사하지 않으려면, +전송이 끝날 때까지 원본의 수명도 이어져야 한다. 느린 수신자가 있으면 데이터뿐 아니라 +그 결과를 유지하는 DB 자원도 살아 있다. 본문 길이·동시 연결·대기 시간을 함께 제한했다. +이 방식이 복사하는 방식보다 전체적으로 유리한지는 이번 검사로 결정하지 않는다. + +## 실제 작성 흐름 + +기존 `.pure` 정의에서 도형을 움직인 뒤 CLI의 `connect`, `push 0`으로 저장한다. +빈 실행기를 열어 `connect`, `pull`하면 적용 코드·현재 값·초안이 복원된다. 서버를 재시작한 +뒤에도 남는다. 다른 실행기가 먼저 갱신하면 이전 revision의 쓰기를 거절해 덮어쓰지 않는다. +실행 방법은 [구현 README](../../prototypes/connections/README.md)에 있다. + +숫자 VM 자체에 DB나 네트워크를 넣지는 않았다. 네이티브 연결 경로는 C17로 작성했고 기존 +Node/Wasm CLI를 재사용했다. C는 현재 SQLite·POSIX 연결을 직접 확인하기 위한 구현 선택이다. +언어 선택의 성능 우위를 주장하거나 기존 Zig 코어를 다시 작성한 것이 아니다. + +## 연결에서 구현한 계약 + +| 경계 | 구현 | 실패·완료 | +|---|---|---| +| 요청 → 입력 구간 | Content-Length 확인 후 한도 안에서 할당하고 부분 수신 | 불완전한 본문은 DB에 제출하지 않고 회수 | +| 입력 구간 → DB | 같은 바이트 구간을 `SQLITE_STATIC`으로 바인드 | 모든 경로에서 statement를 먼저 해제한 뒤 입력을 회수 | +| DB 변경 → 응답 | revision 검사와 갱신을 트랜잭션으로 실행 | COMMIT 뒤에만 성공 응답; 실패·충돌은 기존 저장 내용 유지 | +| DB 결과 → 전송 | 같은 읽기 스냅샷에서 크기를 확인하고 결과 BLOB을 대여 | 별도 응답 본문 사본 없이 헤더·본문 구간을 순서대로 전송 | +| 전송 → 회수 | 부분 전송 위치와 소유자 해제 함수를 연결 | 완료·연결 오류·기한 종료에서 원본을 한 번 회수 | + +SQLite의 바인딩 규칙에 따라 입력 포인터를 statement 종료까지 유지했다. +[공식 바인딩 수명 규칙](https://www.sqlite.org/c3ref/bind_blob.html). +조회 포인터도 변환·step·reset·finalize로 무효화될 수 있으므로 전송 중에는 해당 statement를 +진행하거나 해제하지 않는다. [공식 결과 포인터 수명 규칙](https://www.sqlite.org/c3ref/column_blob.html). +본문 전송 완료는 `send`가 바이트를 받아들인 시점이다. 상대 앱의 처리 완료까지 보장하지 않는다. + +DB 커밋과 응답 전달은 별개다. 응답을 읽지 않은 클라이언트가 끊어져도 이미 끝난 커밋은 +남는다. 조건부 갱신으로 오래된 revision의 재시도를 거절하지만, 임의 RPC의 exactly-once나 +분산 트랜잭션을 제공하지 않는다. [SQLite 트랜잭션 경계](https://www.sqlite.org/lang_transaction.html). + +## 검증과 한계 + +Linux AArch64, GCC 14.2.0, SQLite 3.46.1에서 확인했다. + +- 부분 송수신, 이진 본문, 버전 충돌, DB 잠금·오류, 불완전한 요청, 다른 쓰기 중의 읽기 + 스냅샷, 수신 종료, 자원 포화·기한 종료, 실제 작성물 복원·재시작 등 **15개 통합 시나리오**. +- 코어 검사: 전송 중 조기 해제 금지·중복 회수 방지, 저장 실패 후 내용 보존, revision 소진, + 빈 본문과 읽기 예산 검사. 일반 빌드와 ASan·UBSan 빌드 모두 통과했다. +- 기존 CLI 회귀 검사 8개 통과. 복원 실패·누르고 있는 입력이 있을 때 로컬 상태와 원격 + revision이 바뀌지 않는 것도 통합 검사에 포함했다. +- 검사 종료 후 연결과 보유 본문이 모두 0이고, 본문 접수 한도와 연결 슬롯 수를 넘지 않았다. + 이는 지정한 자원의 수명·한도 검사이며 전체 RAM 상한이나 처리 성능 측정이 아니다. + +SQLite 내부 할당·복사, 페이지 캐시·WAL, OS 버퍼, 클라이언트의 직렬화 비용은 남는다. +수신 헤더에 함께 들어온 본문 접두부도 한 번 복사한다. 장기 읽기는 WAL checkpoint 진행을 +늦출 수 있어 디스크 비용도 포함한 판단이 필요하다. [SQLite WAL 읽기·checkpoint 규칙](https://www.sqlite.org/wal.html#concurrency). +DB 호출은 아직 동기식이므로 디스크 작업 중 다른 연결 처리가 지연될 수 있다. macOS 실행, +전원 장애·디스크 고장, 원격 배포, 인증·TLS, 범용 HTTP/RPC, 네이티브 UI는 검증 범위 밖이다. +동적 링크한 SQLite 자체를 sanitizer로 다시 빌드한 것은 아니다. + +[원자료·재현](../../prototypes/connections/results/2026-09-23-native-connections/README.md), +[전송 수명](../../prototypes/connections/flow.c), +[DB 연결](../../prototypes/connections/store.c), +[호스트](../../prototypes/connections/server.c), +[기존 CLI 연결](../../prototypes/workbench/remote.mjs). + +## 다음 결정 + +현재 경로와 바이트 구간의 수명은 네이티브 코드에 고정되어 있다. Pure 정의에서 이 연결을 +작성하거나 실행 준비 단계에서 불필요한 연산·전달을 제거하는 기능은 아직 없다. +다음은 **이 저장 작업의 입력·검증·외부 효과·응답 관계를 실행 가능한 연결 정의로 분리하는 것**이다. +기존 문법·숫자 VM을 그대로 강제하지 않고, 외부 효과를 재계산하거나 롤백했다고 잘못 표현하지 +않도록 범위를 정한다. 소비자의 요구와 수명에서 판단 가능한 전달 비용부터 다룬다. +새 DB·RPC 기능을 병렬로 늘리거나 종료한 공유/복사 벤치마크를 다시 실행하지 않는다. diff --git a/docs/research/019-byte-storage.md b/docs/research/019-byte-storage.md new file mode 100644 index 0000000..faefecf --- /dev/null +++ b/docs/research/019-byte-storage.md @@ -0,0 +1,84 @@ +# 019 — SQL을 제거한 바이트 접근·저장 경로 + +> 후속 [020](020-prepared-connections.md)에서 저장·응답 구간을 작성 파일로 정의하도록 +> 확장했다. 아래는 그 이전 구현의 기록이며 현재 실행에는 `--program FILE`도 필요하다. + +2026-09-23. **RAM·storage를 SQL에 맞추지 않는다는 요구에 따라 활성 구현의 SQLite를 +제거했다.** 기존 CLI의 저장·복원 흐름은 유지하고 서버를 호출자의 바이트를 직접 기록하는 +C17/POSIX 어댑터로 교체했다. 새 저장 기술의 성능을 입증한 연구 결과가 아니라, 잘못 +도입한 내부 모델을 제거하고 연결의 의미를 확인한 구현 결과다. + +사용자는 이전처럼 작업물을 만들고 `push`·`pull`로 저장·복원한다. 서버 실행 옵션은 +`--db FILE`에서 `--store DIRECTORY`로 바뀌었다. 기존 파일은 자동 변환하거나 덮어쓰지 않는다. +실행 예는 [구현 README](../../prototypes/connections/README.md)에 있다. + +## 내부 모델의 기준 + +필요한 데이터의 범위·접근·갱신·수명에서 배치와 실행 경로를 결정한다. 테이블·행·질의 +실행기에 맞춘 뒤 다시 소비자의 표현으로 바꾸는 단계를 필수로 두지 않는다. DB는 데이터 +접근·일관성·영속성을 다룬다는 범위 이름이며 SQL 엔진 채택을 뜻하지 않는다. + +이번 경로는 RAM에 수신한 본문과 저장 파일의 읽기 범위를 같은 `PureSpan`으로 전달한다. +소비자는 포인터와 길이를 사용하며, 소유자는 소비 종료까지 데이터를 유지한다. 저장소의 +주소를 영속 포인터로 기록하거나 RAM과 저장 매체의 물리적 성질이 같다고 가정하지 않는다. +타입·의미 검증, 표현 변환이 필요한 경계, 공개·영속화·전송 완료의 차이는 남는다. + +현재 Workbench는 JSON 전체를 저장·복원한다. 이 경로에서 직렬화를 제거했다고 주장하지 +않으며, JSON이나 `PureSpan`만으로 전체 애플리케이션의 데이터 표현을 완성한 것도 아니다. +파일별 전체 교체는 지금 작업의 어댑터 선택이다. Pure 전체를 키-값 저장소로 고정하지 않는다. + +## 구현한 경로 + +쓰기: 수신 구간 → 새 파일에 원본 바이트 기록 → 파일 fsync → 원자적 이름 교체 → +디렉터리 fsync → 성공 응답. 버전 비교와 공개는 디렉터리별 쓰기 잠금 안에서 수행한다. +파일의 24 B 헤더는 식별자·버전·본문 길이만 담는다. 본문은 변환하지 않는다. + +읽기: 파일·길이 검사 → 필요한 범위의 읽기 전용 매핑 → 부분 전송 → 매핑 해제. +저장 API는 오프셋·길이 접근을 지원하며 현재 HTTP 경로는 전체 본문을 요청한다. +페이지 정렬에 필요한 접두부도 매핑하므로 요청한 바이트 수가 실제 매핑/RSS와 같지는 않다. +파일 디스크립터는 매핑 후 닫을 수 있다. [mmap 계약](https://man7.org/linux/man-pages/man2/mmap.2.html). + +저장 버전을 제자리에서 수정하지 않고 새 파일로 교체해, 읽고 있는 이전 버전과 새 요청이 +보는 버전을 분리한다. 이는 불변 파일과 이름 교체의 조합이며 `MAP_PRIVATE`만으로 외부 +파일 변경까지 격리되는 것은 아니다. [rename 계약](https://man7.org/linux/man-pages/man2/rename.2.html). + +| 쓰기 결과 | 의미 | +|---|---| +| 예상 버전 불일치 | 충돌, 기존 바이트 유지 | +| 공개 전 실패 | 기존 바이트 유지, 이 호출의 임시 파일 제거 시도 | +| 공개 후 디렉터리 fsync 실패 | 새 바이트는 보이지만 영속화 확인 불가; `PURE_UNCERTAIN` / `durability_unknown` | +| 모든 동기화 성공 | 어댑터의 저장 성공; 상대 앱의 응답 수신 완료와는 별개 | + +파일 fsync만으로 디렉터리 항목의 동기화를 대신할 수 없어 두 시점을 구분했다. +[fsync 계약](https://man7.org/linux/man-pages/man2/fsync.2.html). 이 순서를 구현했다는 사실과 +실제 전원 장애에서 파일시스템·장치의 내구성을 검증했다는 주장은 별개다. + +## 확인한 것과 남은 비용 + +Linux AArch64에서 일반 빌드와 ASan·UBSan 빌드가 모두 통과했다. SQL 라이브러리 링크는 없다. + +- 저장 단위 검사: 범위·페이지 경계·예산, 갱신 중 이전 참조, 충돌·빈 본문·버전 소진, + 잘못된 저장 파일, 공개 전후 실패 주입, 공개 전 프로세스 종료와 재개. +- 실제 TCP 통합 16개 시나리오: 기존 작성물 복원, 두 프로세스의 쓰기 경쟁, 부분 송수신, + 읽는 중 다른 저장, 연결 종료, 자원 포화와 회수, 응답 미수신, 재시작 등. +- 검사 종료 후 활성 연결과 참조 본문 길이는 0이다. 현재 기록은 지정한 자원의 정확성 + 검사이며 지연·처리량·전체 RAM 비교가 아니다. + +현재 경로는 별도 응답 본문 사본을 만들지 않고 SQL 엔진도 경유하지 않는다. 커널 복사· +페이지 폴트·파일 동기화·클라이언트 직렬화는 남는다. 동기 파일 작업은 다른 연결의 처리를 지연시킬 수 있다. +이전 매핑은 이전 파일의 디스크 수명도 연장한다. 공개 전 종료로 남은 임시 파일을 자동으로 +정리하지 않으므로 장기 디스크 사용량의 상한도 아직 보장하지 않는다. 본문 체크섬·부분 쓰기· +다중 객체 트랜잭션은 없고, macOS·전원 장애·디스크 고장도 검사하지 않았다. + +[원자료와 재현](../../prototypes/connections/results/2026-09-23-byte-storage/README.md), +[직접 저장 구현](../../prototypes/connections/store.c), +[구간·수명 API](../../prototypes/connections/store.h). +018의 원자료는 당시 SQLite 구현의 기록으로 별도 보존한다. + +## 다음 결정 + +현재 연결 순서는 여전히 C에 고정돼 있다. 다음은 **필요한 데이터 범위·연산·외부 효과· +소비 완료의 관계를 실행 가능한 Pure 정의로 표현하는 것**이다. 이미 있는 작업의 저장과 +응답 경로에서 시작하고, 공개됐지만 영속화가 불확실한 결과를 단순 실패나 롤백으로 +합치지 않는다. 데이터의 배치는 실제 소비·갱신 패턴에서 결정하며, 새 쿼리 엔진이나 +SQL 대체 제품 개발을 다음 목표로 추가하지 않는다. diff --git a/docs/research/020-prepared-connections.md b/docs/research/020-prepared-connections.md new file mode 100644 index 0000000..89039b1 --- /dev/null +++ b/docs/research/020-prepared-connections.md @@ -0,0 +1,87 @@ +# 020 — 작성한 저장·응답 관계에서 실행 경로 준비 + +> 후속 [021](021-field-connections.md)에서 이름 기반 필드와 입력 값에 의존하는 범위를 추가했다. +> 이 문서의 구조체 크기와 상수 구간 한정 설명은 당시 구현의 기록이다. + +2026-09-23. **이제 C 서버를 고치지 않고 저장할 구간과 응답할 구간을 `.pure`에 작성할 수 +있다.** 시작 때 별칭과 중첩 구간을 최종 접근 범위로 합치고, 요청 처리에는 그 결과만 쓴다. +응답 소비자가 필요한 범위가 실제 파일 매핑까지 전달된다. 작성 관계가 실행 경로에 영향을 +주는 첫 제한된 구현이다. 문법을 추가한 사실이나 새 알고리즘의 성능 우위를 성과로 삼지 않는다. + +기존 작업물 전체를 저장·복원하는 [storage.pure](../../prototypes/connections/examples/storage.pure)를 +기본 사용 예로 유지했다. [excerpt.pure](../../prototypes/connections/examples/excerpt.pure)를 고치면 +같은 서버 실행 파일로 다른 구간을 응답할 수 있다. 실행 중 교체는 지원하지 않으므로 재시작이 +필요하다. [실행·작성 방법](../../prototypes/connections/README.md). + +## 작성 관계가 바꾼 것 + +```text +on get { + let object = store.read(request.key); + let window = range(object.body, 16, 64); + let excerpt = range(window, 16, 16); + reply(object, excerpt); +} +``` + +이 정의의 준비 결과는 다음과 같다. + +- 파일 길이는 **80 B 이상**이어야 한다. 최종 응답에 들어가지 않는 부모 구간의 조건도 남는다. +- 응답 범위는 **오프셋 32부터 16 B**다. 파일 헤더를 확인한 뒤 이 범위만 매핑한다. +- 별칭·중간 구간을 위한 실행 노드나 본문 배열은 만들지 않는다. 매핑·최종 주소 준비·검사는 남는다. +- 파일의 존재·형식 오류와 응답의 revision을 유지한다. 범위 검사와 매핑은 같은 파일 버전에서 수행한다. + +실제 1 MiB 객체에서 이 16 B를 응답했고 논리적 본문 접수 예산도 16 B로 처리했다. +이는 요구 범위가 저장 경로까지 전달됐다는 정확성 확인이다. 실제 매핑 페이지·RSS·디스크 +사용량이 16 B라는 뜻이 아니며, 전체 응답과의 처리량 비교도 아니다. + +쓰기에서도 입력의 일부를 저장하고 같은 부분을 응답할 수 있다. 그 경우 수신 버퍼를 +저장 호출 직후 해제하지 않고 전송 완료·중단까지 유지한다. 요청에서 일부만 참조하더라도 +원본 수신 할당 전체를 보유하는 비용은 남는다. 사용자는 복사나 해제 콜백을 작성하지 않는다. + +## 제거하면 안 되는 작업 + +최종 `[32,48)`이 유효해도 부모 `[16,80)`이 유효하지 않으면 실패해야 한다. 사용되지 않는 +`range`도 길이 부족을 관찰할 수 있으므로 검사까지 제거하지 않는다. 현재 모든 범위 오류는 +같은 실패 종류이며 같은 원본에 대한 최소 길이 조건을 최댓값 하나로 합칠 수 있다. +구간별 오류 위치·다른 효과·서로 다른 버전을 관찰하는 연산이 생기면 이 규칙을 그대로 +확장할 수 없다. + +쓰기의 뷰 정의는 저장 효과보다 앞에 둔다. 실패할 입력으로 저장한 뒤 나중에 오류를 +발견하는 것을 막는다. 본문 없는 응답도 저장 효과나 읽기 대상의 존재 검사를 없애지 않는다. +공개 후 영속화 불확실 결과는 기존 `durability_unknown`으로 전달하며 성공으로 바꾸지 않는다. + +현재 서버는 준비된 범위·조건을 사용해 직접 저장 API와 전송기를 호출한다. 요청마다 +노드 목록을 순회하거나 소스를 다시 해석하지 않는다. `--check-program`의 +`runtime_view_steps: 0`은 이 구현 구조의 설명값이며 CPU 명령 수나 실행 중 측정값이 아니다. +일반/검사 빌드에서 준비된 프로그램 구조체는 112 B다. 이는 전체 서버·컴파일러 메모리가 아니다. + +## 검증과 범위 + +Linux AArch64에서 일반 빌드와 ASan·UBSan 빌드가 모두 통과했다. + +- 기존 실제 TCP 저장·Workbench 복원 등 16개 시나리오와 새 작성·범위·효과·수명 시나리오 + 6개를 합쳐 22개. SQL 의존성은 다시 도입하지 않았다. +- 중간 부모의 범위 오류, 쓰지 않는 뷰의 범위 오류, 본문 없는 읽기의 파일 오류를 보존한다. +- 입력을 돌려주는 응답의 부분 전송·완료·중단과 원본 회수, 중단 후 저장 내용 유지. +- 작성 오류는 저장소 생성·네트워크 접수 전에 거절한다. 파싱 실패는 준비 결과를 변경하지 않는다. +- 순서대로 범위를 검사하는 참조 계산과 준비된 범위의 결과·실패를 경계 조건에서 대조했다. + 이 단위 검사는 실행 속도 비교가 아니다. + +[원자료·소스 사본](../../prototypes/connections/results/2026-09-23-prepared-connections/README.md), +[준비 구현](../../prototypes/connections/program.c), +[실제 실행 경로](../../prototypes/connections/server.c), +[작성 계약](../design/004-prepared-connections.md). + +지금은 등록된 읽기·쓰기·응답과 상수 바이트 범위에 한정한다. 요청당 효과는 하나이며, +숫자/화면 DSL과도 파서를 통합하지 않았다. 동적 범위·조건 분기·피드백·임의 연산·다중 소비자· +여러 외부 효과·비동기 저장을 지원하는 일반 실행 모델은 아직 아니다. macOS 실행, 실제 +전원 장애, 전체 지연·RAM 우위도 검증하지 않았다. + +## 다음 결정 + +현재는 작성자가 바이트 위치를 알아야 한다. 이 부담을 제품의 정상적인 사용성으로 굳히면 +안 된다. 다음에는 **실제 데이터의 필드·연산·소비 관계를 표현하고, 그 관계에서 배치와 +접근을 정하는 것**을 다룬다. SQL·범용 객체를 먼저 정하지 않고 실제 앱 데이터에서 필요한 +조회·갱신과 외부 형식의 변환 지점을 찾는다. 이번 상수 구간 데모의 크기나 벤치마크 행렬은 +늘리지 않는다. 시간 진행과 효과 재실행 의미가 필요한 피드백은 별도 계약 없이 추가하지 않는다. diff --git a/docs/research/021-field-connections.md b/docs/research/021-field-connections.md new file mode 100644 index 0000000..ad1e98e --- /dev/null +++ b/docs/research/021-field-connections.md @@ -0,0 +1,89 @@ +# 021 — 필드와 입력 값으로 정하는 저장·응답 + +2026-09-23. **바이트 위치를 연산마다 직접 쓰지 않고, 필드 이름과 실제 입력 값으로 저장할 +내용을 정할 수 있게 했다.** 문서·선택 시작·길이를 받아 선택한 내용만 저장하고 응답하는 +네이티브 경로를 구현했다. 한글·이모지가 있는 UTF-8 파일을 예제 클라이언트에서 선택하고 +다시 조회해 같은 바이트가 남는 것을 확인했다. + +이 단계는 작성 표현과 실제 계산·저장·소비를 연결한 기능 결과다. 범용 타입 시스템이나 +새 직렬화 표준을 확정한 것이 아니며 성능 우위도 주장하지 않는다. + +## 사용자가 작성하는 관계 + +[selection.pure](../../prototypes/connections/examples/selection.pure)는 경계의 필드를 선언한다. + +```text +layout Selection { + start: u32le; + count: u32le; + document: bytes; +} + +on put { + let input = view(Selection, request.body); + let selected = range(input.document, input.start, input.count); + let saved = store.write(request.key, request.revision, selected); + reply(saved, selected); +} +``` + +`layout`은 이 입력 경계의 배치다. 선언 순서와 타입 폭으로 접근 위치를 정하며, RAM 전체나 +저장소를 같은 레코드로 강제하지 않는다. 이 예제의 저장 본문은 선택한 원본 문서 바이트이고 +입력의 start·count 필드는 저장되지 않는다. 기존 전체 바이트 저장 경로에는 layout도 필요 없다. + +이름·타입·별칭은 서버 시작 때 해소한다. 요청에서는 두 u32 값을 정렬되지 않은 위치에서도 +little-endian으로 읽고, 범위를 검사한 뒤 원본의 해당 구간을 참조한다. 레코드 객체·필드 사전· +선택 본문 사본을 추가하지 않는다. 저장과 응답은 같은 계산 결과를 쓰며 응답 완료까지 원본을 유지한다. + +실제 사용은 [Python 예제 클라이언트](../../prototypes/connections/examples/select-text.py)가 +UTF-8 텍스트 파일과 문자 위치를 받아 요청한다. 클라이언트는 Unicode 코드 포인트 위치를 +바이트 위치로 바꾼다. 서버의 bytes 연산 자체는 UTF-8이나 사용자에게 보이는 글자 덩어리를 +검증하지 않는다. [실행 방법](../../prototypes/connections/README.md). + +## 준비할 수 있는 것과 실행해야 하는 것 + +상수 구간은 기존처럼 합친다. 필드의 실제 값에 의존하는 구간만 준비된 범위 연산 목록에 +남긴다. 예제는 한 단계이며 그 결과에 별칭을 더 붙여도 단계 수는 늘지 않는다. +동적 구간의 하위 구간도 표현할 수 있다. 사용하지 않는 동적 구간의 실패도 관찰 가능한 +동작으로 유지하고, 모든 입력 검사가 성공한 뒤에만 쓰기를 호출한다. + +GET에서는 이름 기반 고정 필드와 가변 꼬리 구간을 선택한다. 꼬리의 길이는 같은 파일 버전의 +전체 크기에서 구하므로 본문 전체를 별도 배열로 읽지 않는다. GET의 숫자 필드 값에 의존하는 +범위는 현재 거절한다. 필요한 숫자와 선택 본문을 같은 버전에서 읽는 계획을 구현하지 않고 +전체 매핑으로 대체하지 않았다. + +지원 타입은 u32le·고정 bytes[N]·마지막 가변 bytes다. 선언은 최대 4개, 필드는 선언당 16개, +입력 값에 의존하는 범위는 8단계다. 버전 식별·마이그레이션·일반 산술·중첩 배치·여러 가변 +필드를 지원하는 최종 애플리케이션 데이터 모델은 아직 아니다. 송수신자가 배치에 합의해야 한다. + +## 비용과 검증 + +Linux AArch64에서 일반 빌드와 ASan·UBSan 빌드가 통과했다. + +- 기존 22개 통합 시나리오에 실제 문서 선택, 잘린 입력·동적 범위, 필드 순서·정렬 변경, + 사용되지 않은 계산의 오류, 저장된 가변 꼬리 조회, 부분 전송 수명을 추가해 28개를 확인했다. +- 선언 순서를 바꾸면 실제 숫자 접근 위치도 달라진다. 고정된 헤더 위치를 서버에 숨겨 넣은 것이 아니다. +- 정렬되지 않은 u32, 여러 바이트에 걸친 수, 최대 u32, 빈 구간·끝 위치·범위 초과를 검사했다. +- 오류가 나면 기존 내용·revision을 유지한다. 느린 응답에서는 선택 본문 길이만 계상하는 대신 + 보유 중인 원본 수신 할당 전체를 계상하고, 완료 뒤 반납한다. + +이 기능에는 비용이 추가됐다. 준비 구조체는 프로그램당 **112 B에서 648 B로 증가**했다. +최대 8개의 동적 범위 계획도 고정 예약하는 현재 구현의 크기이며 전체 RSS·컴파일러 크기가 아니다. +요청 계산에는 고정된 임시 span 배열을 쓰고 이 기능 때문에 힙 객체를 만들지는 않는다. +기존 수신 할당·파일 쓰기·페이지 매핑·커널 복사·클라이언트 인코딩 비용은 남는다. +따라서 이번 결과를 전체 메모리 절감이나 처리량 개선으로 해석하지 않는다. + +[원자료·소스 사본](../../prototypes/connections/results/2026-09-23-field-connections/README.md), +[필드와 범위 준비·계산](../../prototypes/connections/program.c), +[계약](../design/005-field-connections.md). + +macOS 실행, 전원 장애, 대규모 서비스, 네이티브 UI, 현재 숫자/화면 DSL과의 언어 통합은 +검증 범위 밖이다. 저장 효과는 여전히 요청당 하나이고 파일 작업은 동기식이다. + +## 다음 결정 + +현재 입력 값과 소비자를 연결했지만, 저장된 값의 조회·변경·다시 저장은 한 요청에서 작성할 +수 없다. 다음은 **실제 앱의 읽기·변경·저장 경로에서 연산과 버전 관계를 연결하는 것**이다. +읽은 버전과 조건부 쓰기의 관계, 서로 다른 생산자가 제공하는 값의 수명을 먼저 정한다. +숫자 타입·문법을 늘리기보다 기존 실행 코어에서 재사용할 연산과 필요한 외부 효과 경계를 +구체적인 작업에서 결정한다. 이번 문서 선택 데모의 크기를 키운 반복 측정은 하지 않는다. diff --git a/docs/research/022-execution-core.md b/docs/research/022-execution-core.md new file mode 100644 index 0000000..dfe826e --- /dev/null +++ b/docs/research/022-execution-core.md @@ -0,0 +1,72 @@ +# 022 — HTTP 연결에서 분리한 작은 실행 코어 + +2026-09-23. 상태: 저장·범위 선택 프로필의 구현과 정확성 검증. +첫 공개 체크포인트의 일부이며 통합 OS·범용 앱 실행·고성능 RPC 완성 보고가 아니다. + +## 실제로 바뀐 책임 + +이전에는 HTTP `Connection`이 본문 할당·예산·저장 호출·해제를 소유했다. +이제 `runtime.c`가 작업 슬롯과 세대, 입력 예산과 소유권, 준비 대기열, 효과 실행, +결과 보유·회수를 관리한다. 서버는 바이트를 받아 접수된 입력을 채우고, 완료 결과를 전송한다. +본문이 일부만 도착하고 끊기면 준비되지 않은 작업을 해제하므로 저장 효과가 발생하지 않는다. + +코어는 호출자가 제공한 작업 슬롯을 사용한다. 영역마다 스레드·소켓·힙·큰 큐를 만들지 않는다. +준비된 `PureProgram`은 읽기 전용으로 공유할 수 있다. 현재 효과는 영역에 연결한 `PureHost`의 +동기 읽기·쓰기·할당 함수다. 파일·매핑·할당 구현은 `store-port.c`에 있고 HTTP는 `server.c`에 있다. +이 API는 C 내부 연결 계약이며 메모리 격리·안정된 외부 ABI·포괄적 권한 시스템은 아니다. + +준비된 프로그램의 실행을 `evaluate.c`로 분리했다. `libpure-execution.a`는 `runtime.c`와 +`evaluate.c`만 포함한다. 서버는 소스 로딩을 위해 여전히 별도로 `program.c`를 링크한다. +코어 아카이브가 작다는 이유로 서버 전체에서 파서 비용이 사라졌다고 해석하지 않는다. + +## 내부 연결과 수명 + +완료된 결과를 다른 영역의 입력으로 전달하면 코어가 원본 작업을 pin한다. 중간 버퍼 할당· +직렬화·소켓 전송 없이 같은 데이터 구간을 사용하며, 생산자 쪽 호출자는 자신의 티켓을 +먼저 해제할 수 있다. 마지막 소비가 끝나야 원본 메모리·매핑이 반환된다. +한 결과의 여러 소비자 중 하나가 취소돼도 나머지 소비자의 참조는 유지한다. +전달된 입력을 응답에서 사용하지 않는 프로그램은 저장 효과가 끝나면 원본을 일찍 해제한다. + +긴 전달 연결의 회수는 반복문으로 처리한다. 1,024개 연결 검사는 재귀적 해제로 스택이 +증가하지 않고 할당 한 개를 마지막에 한 번 회수하는지 확인하는 수명 검사다. 성능 벤치마크가 아니다. +각 영역의 예산은 자신의 유지 입력·출력에 대한 접수 크레딧이다. 공유된 데이터를 영역별로 +청구하므로 여러 영역의 크레딧을 합해 물리 메모리라고 주장하지 않는다. + +종료는 새 접수를 막고 기존 작업을 배출한다. 진행·완료·대여 작업이 남은 영역의 재시작은 +거절하며, 완전히 배출된 뒤 재시작해도 티켓의 단조 증가 세대는 유지한다. +이전 티켓은 새 작업의 결과를 읽거나 해제할 수 없다. 실행 전 취소와 효과 실행 후 결과는 구분한다. + +## 검증 근거 + +[원시 결과](../../prototypes/connections/results/2026-09-23-execution-core/)의 native·ASan/UBSan +빌드에서 기존 28개 실제 TCP 시나리오와 실행 코어 검증을 통과했다. +기존 범위 의미 비교 3,584개와 필드 경계 사례 48개, 저장 실패·매핑·전송 검사도 유지했다. + +추가로 확인한 것은 다음과 같다. + +- 준비되지 않은 입력·취소·대기열 제거는 저장 효과를 실행하지 않는다. +- 슬롯·바이트 예산 포화, 할당 실패, 제공되지 않은 쓰기 권한은 접수/실행 경계에서 처리한다. +- 호스트 호출 중 같은 영역의 재진입을 거절한다. 읽기 호스트가 반환 한도를 어겨도 자원을 회수한다. +- 완료 결과의 두 소비자 중 하나를 취소하고 생산자의 티켓을 먼저 해제해도 나머지 결과는 유효하다. +- 실제 입력 숫자로 선택한 `bcd`를 첫 영역에 저장하고 두 번째 영역으로 전달해 별도 저장소에 보존한다. +- 파일을 매핑한 결과를 다른 영역에 넘긴 뒤 원래 저장 객체를 교체해도 이전 내용으로 완료한다. +- 잘못된 입력 범위는 실제 저장 파일을 만들지 않는다. 종료 뒤 모든 영역의 작업·크레딧은 0이다. + +## 크기와 한계 + +Linux AArch64, GCC `-O2`에서 확인한 구조체 크기는 영역 192 B, 작업 슬롯 224 B, +티켓 24 B, 공유 가능한 준비 프로그램 648 B다. 본문·호스트 자원·스택·allocator·RSS는 별도다. +GNU `size`의 코어 두 오브젝트 `text` 합은 5,672 B이며 데이터·BSS는 0 B다. +이는 코드/읽기 전용 섹션 등에 대한 도구 수치이며 전체 프로세스 RAM이나 아카이브 파일 크기가 아니다. +코어 아카이브의 외부 심볼은 `memcpy`, `strlen`과 아카이브 내부에서 해결되는 +`pure_program_input`이다. 파서·할당기·파일·소켓·스레드 API를 직접 참조하지 않는다. + +8개 작업 슬롯을 추가한 서버 구조체는 이전 39,824 B에서 41,488 B로 **1,664 B 증가**했다. +HTTP 버퍼가 여전히 대부분을 차지한다. 이 변경은 RAM 절감 결과가 아니라 영역별 작업·수명 +관리를 추가한 비용을 명시한 것이다. 선택 기능을 제거한 전체 실행물과 실제 앱의 비용 검증은 남았다. + +현재 쓰기는 동기 파일 호출이며 오래 걸리는 효과를 선점하거나 다른 실행 영역의 지연을 +보장하지 못한다. core 제어 API는 한 소유 스레드에서만 사용한다. 입력은 여전히 전체 본문을 +받은 뒤 실행하며, 작성 프로필도 get/put·하나의 저장 효과에 한정된다. 소스 문법의 HTTP 명칭 +제거, 일반 앱 연산, 비동기 효과, 자체 RPC·TLS, 네이티브 UI, macOS 실사용 검증은 남아 있다. +이 결과로 Kitex·QUIC보다 빠르거나 네이티브 코드를 강제 격리한다고 주장하지 않는다. diff --git a/docs/research/023-document-edits.md b/docs/research/023-document-edits.md new file mode 100644 index 0000000..d2bcd5e --- /dev/null +++ b/docs/research/023-document-edits.md @@ -0,0 +1,102 @@ +# 023 — 문서 부분 변경과 코어 크기 제한 + +2026-09-23. Linux AArch64, GCC 14.2.0. 제한된 저장 프로필의 구현·정확성·구조 비용 기록이다. +첫 공개 체크포인트는 진행 중이며 네이티브 UI, 통합 앱 작성 모델, 보안 RPC는 아직 남아 있다. + +## 이번 결정 + +실제 편집에서는 문서 전체를 다시 입력하기보다 바꿀 위치·삭제 길이·삽입 내용으로 변경을 +표현해야 한다. 이 효과를 기존 작성·접수·완료·회수 경로에 추가했다. 별도 편집 VM, 서비스 +관리자, 도메인별 스레드·힙·소켓은 만들지 않았다. 코어가 선택 기능의 구현을 끌어오지 않는지, +추가한 관리 비용이 얼마인지 함께 검사한다. + +## 작성과 실행 + +[editor.pure](../../prototypes/connections/examples/editor.pure)는 입력의 두 `u32le` 필드와 +삽입 바이트를 다음 효과에 연결한다. + +```text +store.splice(request.key, request.revision, edit.start, edit.remove, edit.insert) +``` + +효과는 저장된 값의 `[0,start)`와 삽입 내용, `[start+remove,end)`를 이어 새 버전을 공개한다. +예상 버전과 저장 값의 범위 검사는 같은 작성자 잠금 안에서 수행한다. 존재하지 않는 값은 +예상 버전 0, 위치 0, 삭제 길이 0일 때 만들 수 있다. 빈 변경도 성공하면 버전이 증가한다. +바이트 위치이며 문자열·문자소·UTF-8 유효성을 자동 검사하는 연산은 아니다. + +한 핸들러에 저장 효과는 여전히 하나다. 입력 뷰의 검증은 효과 전에 완료하며 반환 본문은 +입력 바이트를 빌린다. 보통 쓰기 권한만 제공된 호스트에서 splice를 접수하면 거절한다. +저장 성공 이후 응답이 끊겨도 효과를 되돌리지 않는다. 파일 공개 후 디렉터리 동기화가 실패하면 +`durability_unknown`을 반환한다. 같은 예상 버전으로 무조건 재시도하면 충돌할 수 있다. + +네이티브 C CLI `pure-edit`가 같은 준비 프로그램과 실행 코어를 사용한다. `edit`은 준비된 +필드 위치에 인자를 채운다. 선언 순서를 바꾸거나 숫자가 비정렬 위치에 있어도 대응한다. +고정 위치·동적 범위 등 편의 명령으로 표현할 수 없는 입력은 `apply`로 원시 입력 파일을 +전달한다. 로컬 실행에는 소켓이 필요 없다. 별도 프로세스의 HTTP 클라이언트와도 같은 +저장 버전으로 변경·충돌·복원을 확인했다. 이 CLI를 네이티브 UI 완성으로 세지 않는다. + +## 메모리와 저장 비용을 구분한다 + +1 MiB 이진 문서 중 3 B를 바꾸는 경로에서 입력은 위치 4 B + 삭제 길이 4 B + 삽입 3 B다. +네이티브 CLI와 실제 TCP 서버 모두 **입력 크레딧 11 B**로 실행했고, 서버에서 더 큰 입력은 +효과 전에 거절했다. 저장소는 변경하지 않은 부분을 **4 KiB 호스트 스택 버퍼**로 옮긴다. +새 문서 전체를 앱의 힙 버퍼로 조립하지 않지만 **저장 파일 전체는 여전히 다시 쓴다.** + +11 B는 전체 RAM·RSS·피크 메모리가 아니다. 코어 구조체·스택·파서·allocator·HTTP 버퍼· +매핑 페이지·커널 캐시·파일 I/O가 따로 있다. 재작성의 디스크 대역폭이나 동기 `fsync` 지연을 +없앤 결과도 아니다. 현재 입력 본문 자체는 전부 받은 뒤 실행한다. 다음 저장 배치 최적화는 +실제 앱에서 재작성 비용이 문제가 되는 근거에 따라 결정한다. + +## 코어가 커지는 것을 검출한다 + +`make core`, `make editor`, `make server`로 구성을 각각 빌드한다. 깨끗한 출력 디렉터리에서 +`make core`가 만드는 것은 `runtime.o`, `evaluate.o`, `libpure-execution.a`뿐임을 확인했다. +편집기와 서버는 선택한 저장·소스 파서를 추가로 포함한다. 실행 전용 코어에는 필요하지 않다. + +`make check-core`는 [예산 파일](../../prototypes/connections/core-budget.json)에 따라 현재 +프로필의 GNU `size` text 합 **6 KiB**, data/BSS **0 B**, 아래 구조체의 현재 크기를 상한으로 +검사한다. 아카이브 멤버와 미해결 외부 심볼도 확인한다. 전용 힙·파일·소켓·스레드 API를 +연결하는 변경은 검사 대상이다. 6 KiB는 현재 5,932 B의 증가를 감지하기 위해 정한 개발 상한이며 +범용 런타임 전체의 크기 약속이나 실측 RAM이 아니다. 다른 플랫폼·도구에서는 다시 검증한다. +확장에 필요하다는 이유만으로 상한을 자동으로 올리지 않고 기능 위치와 비용을 먼저 검토한다. + +| 항목 | 022 | 현재 | 증가 | +|---|---:|---:|---:| +| 실행 코어 두 오브젝트의 `size text` | 5,672 B | 5,932 B | 260 B | +| 실행 영역 구조체 | 192 B | 200 B | 8 B | +| 작업 슬롯 구조체 | 224 B | 224 B | 0 B | +| 공유 가능한 준비 프로그램 | 648 B | 680 B | 32 B | +| 티켓 | 24 B | 24 B | 0 B | +| HTTP 서버 구조체 | 41,488 B | 41,528 B | 40 B | + +이번 추가 비용은 선택한 효과를 호출하는 분기·인자 읽기, 호스트 함수 포인터 하나, +준비된 두 위치 값이다. splice를 사용하지 않는 프로그램에도 현재 준비 구조체의 증가가 +적용된다. 따라서 선택 저장 구현을 분리했다는 이유로 모든 관리 비용까지 선택적이라고 하지 않는다. + +코어의 실제 외부 심볼은 `memcpy`, `strlen`이다. 크기 상한을 낮추면 실패하고, 검사용 아카이브에 +`malloc` 의존성·4 KiB 전역 상태를 추가하면 실패하는 것도 확인했다. 계측 도구 자체는 코어에 없다. + +전체 선택 실행물의 수치는 다음과 같다. 둘 다 소스 파서를 포함하고 직접 동적 의존성은 +이 환경의 `libc.so.6`다. 공유 라이브러리·로더의 메모리를 아래 파일 크기에 포함한 것은 아니다. + +| 구성 | `size` text/data/bss 합 | 심볼 제거 후 ELF 파일 | +|---|---:|---:| +| 네이티브 편집 CLI | 36,767 B | 67,904 B | +| 루프백 HTTP 서버 | 43,817 B | 68,000 B | + +파일 정렬·헤더와 디버그 정보가 있으므로 디스크 파일 크기, 섹션 크기, 구조체 크기는 서로 다르다. +실제 앱의 유휴·피크 메모리와 선택 라이브러리 전체 비용 검증은 여전히 남아 있다. + +## 검증과 남은 범위 + +[동결된 근거](../../prototypes/connections/results/2026-09-23-document-edits/)에 일반·ASan/UBSan +빌드 결과, 소스 37개와 해시, 코어 및 선택 빌드 크기를 보관했다. 각 빌드의 통합 시나리오 36개, +기존 구간 의미 비교 3,584개·필드 사례 48개, 실행 수명 검사를 통과했다. 통합 검사에는 +네이티브 CLI와 실제 TCP를 통한 왕복·두 작성자의 동일 버전 경쟁·불완전 입력이 포함된다. +새 저장 단위 검사는 이진 접두부/접미부 보존·이전 매핑 유지·위치 및 크기 초과·버전 소진· +공개 전 실패·공개 후 내구성 불확실·임시 파일 회수를 확인한다. 모든 서버 종료 시 작업과 +본문 크레딧은 0이다. 이는 정확성과 크기 경계의 증거이며 처리량 우위나 실제 정전 시험이 아니다. + +여전히 단일 소유 스레드, 동기 저장, get/put 작성 프로필이다. 문자열 편집 UI·일반 연산· +비동기 효과·작성 모델 통합·자체 보안 RPC·macOS 실사용은 [체크포인트](../../CHECKPOINT.md)에 +남긴다. 이 기능과 검사를 새 범용 프레임워크의 근거로 확장하지 않는다. diff --git a/docs/research/024-native-app-instances.md b/docs/research/024-native-app-instances.md new file mode 100644 index 0000000..fee556d --- /dev/null +++ b/docs/research/024-native-app-instances.md @@ -0,0 +1,109 @@ +# 024 — 기존 작성물을 네이티브 앱 상태·저장 경로에 연결 + +2026-09-23. Linux AArch64, Zig 0.15.2 / GCC 14.2.0. +첫 공개 체크포인트 진행 중이다. 이번 실행 호스트는 터미널이며 네이티브 창은 아직 없다. + +## 바뀐 경계 + +기존 Workbench의 계산 실행기는 전역 `engine` 하나를 사용했다. Wasm 인스턴스마다 메모리가 +분리되는 브라우저에서는 여러 작성물을 실행할 수 있었지만, 같은 코드를 네이티브 라이브러리로 +붙이면 앱마다 독립된 상태를 제공하지 못했다. + +실제 계산을 `engine.zig`의 명시적인 `Engine` 인스턴스로 옮겼다. 기존 `core.zig`는 브라우저 +ABI와 회귀 검사를 유지하는 얇은 연결부다. 네이티브 `native.zig`는 호출자가 제공한 정렬된 +메모리에 인스턴스를 초기화하고 C 함수로 접근한다. 네이티브 모듈에는 기본 VM 인스턴스, +할당기, 파일·소켓·스레드가 없다. 실행 정의의 준비·실패·복원·평가 기록도 해당 인스턴스에 속한다. + +이 계산 모듈은 **앱이 선택하는 기존의 제한된 scalar 실행기**다. 모든 Pure 실행 영역에 +자동으로 포함하는 새 공통 VM이 아니다. 작업 접수·수명·대여·예산을 맡는 C 코어는 변경하지 않았다. + +## 작성에서 네이티브 실행까지 + +새 문법이나 별도 파서를 만들지 않았다. `native/prepare.mjs`가 기존 `compiler.mjs`로 `.pure`를 +준비하고 읽기 전용 C 데이터로 출력한다. 상태·입력 이름, 공유 규칙의 바인딩, 대상 라우팅, +표시 식이 그대로 들어간다. 문자열은 UTF-8 바이트의 C escape로 출력해 소스 문자열이 C 코드가 +되지 않게 한다. 이 데이터와 실행 모듈·호스트를 링크해 독립 실행 파일을 만든다. + +Node·Zig·C 컴파일러는 **빌드 도구**다. 만들어진 실행물은 Node·브라우저·Wasm 없이 구동한다. +실행 시 소스 파서나 AST를 보관하지 않는다. 다만 연산은 기존 준비 명령을 해석하며, 앱의 각 +연산을 기계어로 직접 생성하는 AOT 최적화 컴파일러를 구현한 것은 아니다. 현재 작성 변경은 +다시 빌드해서 반영한다. 실시간 소스 편집·교체는 아직 네이티브 호스트에 연결하지 않았다. + +## 하나의 실행 코어를 통과하는 앱 경로 + +`native/app.c`는 준비된 앱의 입력과 상태를 기존 C 실행 코어에 연결한다. + +```text +입력 → 코어의 접수·예산·작업 슬롯 → 선택한 계산 인스턴스 → 상태/표시 값 +상태 스냅샷 → 코어의 내부 결과 대여 → 조건부 바이트 저장 +저장된 바이트의 매핑 → 코어의 내부 결과 대여 → 검증 후 상태 복원 +``` + +저장과 복원은 기존 `pure_runtime_forward`를 사용한다. 전달을 위한 두 번째 본문 버퍼나 +소켓 직렬화를 추가하지 않는다. 단, **현재 상태를 스냅샷으로 만들 때는 복사한다.** 이후 입력이 +상태를 바꾸어도 저장할 내용이 달라지지 않도록 44 B의 헤더와 상태당 8 B를 별도 소유한다. +같은 데이터의 두 작업이 유지되는 동안 각 작업의 크레딧을 계산하므로 이를 물리 메모리로 합산하지 않는다. + +예제의 상태 25개는 스냅샷 244 B다. 버전 충돌은 상대 값을 덮어쓰지 않고 현재 앱 상태도 +바꾸지 않는다. 복원은 정의 식별자·상태 수·길이·유한한 값을 검사한 뒤 전체 정의와 값을 함께 +설치한다. 실패하면 기존 앱 상태와 채택한 저장 버전은 유지한다. 초기값과 현재 값은 별개라 +복원 후 `reset`은 작성된 초기값으로 돌아간다. + +정의 식별자는 빌드 도구가 만든 SHA-256이다. 의미만 같은 다른 소스나 위치 정보가 달라진 +정의를 자동으로 호환 처리하지 않으며, 정의가 다르면 복원을 거절한다. 이는 인증이나 서명이 +아니다. 현재 스냅샷은 브라우저 Workbench의 JSON 프로젝트 형식과 별개이며 변환기는 미구현이다. +수치 표현도 현재 IEEE binary64 경계에 한정한다. 모든 Pure 데이터를 이 배치로 강제하지 않는다. + +포인터는 입력 그룹이 아닌 실제 대상마다 소유자를 가진다. 같은 라우팅 입력의 두 대상을 +동시에 조작하고 하나만 취소할 수 있다. 다른 소유자의 이동·해제는 거절한다. 키 입력은 여러 +출처가 눌러도 첫 down과 마지막 up만 작성 규칙에 전달한다. 진행 중인 입력이 있으면 저장· +복원을 거절한다. 이벤트의 계산 실패는 상태 쓰기를 원자적으로 되돌리며 실제 입력 해제와 구분한다. +네이티브 명령 인터페이스의 오류 표현·무시 정책 전체가 브라우저 UI와 같다는 주장은 아니다. + +## micro 비용 + +일반 네이티브 라이브러리 설정은 기본 panic 보고·심볼 해석과 compiler-rt 아카이브를 포함했다. +이 선택 모듈에는 그 전체 기능이 필요하지 않았다. `ReleaseSafe`의 범위·오버플로 검사를 유지하고 +예상하지 못한 내부 불변식 위반은 trap으로 중단하도록 바꿨다. 정상적인 작성 오류는 기존 상태 +코드로 반환한다. 실패를 무시하거나 안전 검사를 꺼서 줄인 것은 아니다. 내부 버그 발생 시 +이 모듈이 자체 스택 추적을 출력하지 않는 비용 절충은 남으며, 네이티브 크래시 격리도 아니다. + +필요하지 않은 compiler-rt 전체를 포함하지 않은 뒤 실제 외부 심볼은 `memcpy`, `memset`뿐이다. +초기 아카이브 수치는 동결 자료에 보관했다. 링크 전 아카이브 합을 최종 실행물 크기 감소율로 +해석하지 않는다. + +| 범위 | 확인한 비용 | +|---|---:| +| 공통 C 실행 코어의 GNU `size text` | 5,932 B, 이전과 동일 | +| 선택한 scalar 라이브러리의 `size text` | 33,020 B | +| scalar 라이브러리의 data/BSS | 0 B / 0 B | +| 앱별 scalar 인스턴스 | 20,064 B, 정렬 8 B | +| 앱 컨트롤러 | 928 B, 실행 코어와 작업 슬롯 2개 포함 | +| 앱 입력 값 | 32 B | +| 상태 25개의 저장 스냅샷 | 244 B | +| 라우팅 예제 전체 CLI의 text/data/BSS 합 | 64,883 B | +| 같은 실행물의 심볼 제거 후 ELF 파일 | 68,000 B | + +계산 인스턴스의 20,064 B에는 최대 용량의 준비 정의·교체 작업 공간·상태·평가 기록이 들어간다. +읽기 전용 C 준비 데이터도 실행물에 존재한다. 현재 배열의 고정 예약과 정의 복사 비용을 +없앴다고 하지 않는다. 입력·스냅샷의 호스트 할당, 스택, libc·로더·커널·페이지·유휴/피크 RSS는 +위 구조체 수치와 별개다. 현재 ELF의 직접 동적 의존성은 `libc.so.6`이며 전체 의존 메모리 +비용을 측정한 결과가 아니다. 코어 크기 검사는 그대로 통과한다. + +## 실제로 확인한 범위 + +[동결된 근거](../../prototypes/workbench/results/2026-09-23-native-instances/)에 다음을 보관했다. + +- 기존 Zig 의미 검사 37개, 브라우저/Wasm 및 Node CLI 회귀 검사 87개 통과. +- 기존 카운터·토글·프레임 계산·드래그·공유 대상·숫자 입력과 빈 정의·계산 실패·문자열 경계 등 + 작성물 9개의 입력 156단계에서 네이티브와 Wasm의 상태·표시 값 일치. +- 각 네이티브 작성물을 저장한 뒤 새 프로세스에서 같은 정의로 복원해 값과 표시 식을 확인. +- 같은 프로세스의 인스턴스 3개에서 상태·준비 실패·평가 설정·입력 소유권·예산이 섞이지 않음을 확인. +- 실제 저장소의 버전 충돌, 다른 정의, 비유한 상태 복원 거절과 모든 작업·크레딧 회수 확인. +- 일반 C 호스트와 ASan/UBSan C 호스트에서 위 네이티브 경로 통과. Zig 오브젝트는 둘 다 + `ReleaseSafe`이며 ASan으로 계측된 Zig 빌드라고 주장하지 않는다. + +동시 소유 스레드 간 실행, 네이티브 창·IME·접근성·크기 변경, macOS 실사용은 이번 검증 범위에 없다. +기존 scalar 한도(상태 32, 입력 8, 표시 24, 명령 512)도 유지된다. 대규모 앱 전체를 이 한도에 +맞추라는 뜻이 아니라 이전 작성 모델을 실제 네이티브·저장 경로로 연결한 중간 구현이다. +전체 체크포인트의 일반 앱 표현·비동기 효과·보안 RPC·실제 화면·규모 검증은 계속 남아 있다. diff --git a/docs/research/025-native-authoring-shell.md b/docs/research/025-native-authoring-shell.md new file mode 100644 index 0000000..960ae7a --- /dev/null +++ b/docs/research/025-native-authoring-shell.md @@ -0,0 +1,103 @@ +# 025 — 네이티브 작성 창을 실행 코어에 연결하고 비용을 분리 + +2026-09-24. Linux AArch64, GCC 14.2.0, GTK 4.18.6, Zig 0.15.2. +첫 공개 체크포인트의 중간 결과다. **네이티브 창을 만들었다는 사실만으로 마이크로 앱 런타임이나 +네이티브 UI 모델이 완성된 것은 아니다.** 이번에 확인한 경계는 편집 도구를 붙이면서도 실행 전용 +구성을 이전 크기로 유지할 수 있는지다. + +## 실제 작업 경로 + +`manage.py native --desktop`은 기존 `.pure` 정의와 같은 C 실행 코어·독립 scalar 인스턴스를 +링크한 Linux GTK4 작성 창을 만든다. 왼쪽은 GTK의 `GtkTextView`, 오른쪽은 현재 제한된 +box/circle/text 출력과 입력을 보여 주는 화면이다. 작성자는 소스를 고치고 **Apply source**를 +누른다. 기존 JS 컴파일러는 편집 호스트의 자식 프로세스에서 준비 정의를 만들고, C 컴파일러는 +그 읽기 전용 데이터만 공유 라이브러리로 만든다. 편집 호스트가 이를 열어 후보를 제출한다. + +```text +GTK 입력·편집 → 선택한 C 호스트 → 실행 코어의 작업 접수·완료 → scalar 앱 인스턴스 + ↓ + SQL 없는 상태 저장·복원 +``` + +새 정의 설치도 코어의 `plan` 입력으로 보낸다. 진행 중인 입력·작업이 있으면 거절하고, 같은 이름의 +상태는 이어받는다. 컴파일·정의 검증에 실패하면 이전 실행 정의와 상태가 남는다. 성공해야 소스 +파일을 기록하며, 상태 스냅샷은 별도 **Save state**로 조건부 저장한다. 정의가 달라진 스냅샷은 +자동 호환시키지 않는다. 재시작 때 편집 호스트가 소스를 준비·적용한 뒤 **Load state**를 +요청할 수 있다. 준비된 `.pure`를 실행하는 CLI에는 GTK·Node·컴파일러·동적 모듈 로더가 없다. + +실시간 적용은 **선택한 개발 호스트**의 기능이다. 작성된 실행물의 앱 연산 전체를 AOT 기계어로 +컴파일한 것은 아니다. 로컬에서 신뢰하는 `.pure`와 도구체인을 전제로 하며, 공유 라이브러리 +적재를 격리나 원격 코드 실행 경계로 제시하지 않는다. 소스 문자열은 기존 `prepare.mjs`에서 +바이트 escape로 생성하므로 작성한 문자열을 C 문장으로 삽입하지 않는다. + +소스 입력은 GTK의 일반 텍스트 위젯을 써서 선택·편집·실행 취소를 제공한다. 이 구성은 +[GTK의 텍스트 위젯](https://docs.gtk.org/gtk4/class.TextView.html)과 +[입력 컨트롤러](https://docs.gtk.org/gtk4/input-handling.html)에 의존한다. 별도의 macOS IME· +접근성 실사용 검증을 했다는 뜻은 아니다. 표시는 GTK `DrawingArea`의 콜백이며 그 안에서 +앱 상태를 바꾸지 않는다. 이는 [GTK의 DrawingArea 계약](https://docs.gtk.org/gtk4/method.DrawingArea.set_draw_func.html)을 따른다. + +## 실제 창에서 확인한 동작 + +[근거 폴더](../../prototypes/workbench/results/2026-09-24-native-desktop/)에 Xvfb의 실제 GTK 창을 +조작한 화면과 검사 결과를 보관했다. + +- 세 도형 예제의 녹색 도형을 드래그해 놓고 상태를 저장했다. 새 CLI 프로세스가 같은 스냅샷을 + 복원했으며 `green.x=167.07482993197277`, `green.y=154.82993197278913`, `held=0`이었다. +- 창의 소스 끝에 `text live(20,20,selected,"LIVE: ")`를 추가해 적용했다. 기존 선택·도형 위치가 + 유지됐고 출력 `LIVE: 1`이 나타났다. +- `invalid source line`을 추가하면 `Unknown declaration invalid (73:1)`을 한 줄로 보여 주고, + 직전 화면·상태·저장된 소스를 유지했다. 잘못된 줄을 지우고 다시 적용한 뒤 새 정의의 상태를 + 저장했다. 새 CLI 프로세스는 저장 버전 2에서 기존 도형 위치와 새 `live` 표시 값을 복원했다. +- 창을 1320×720에서 900×600으로 줄인 뒤 같은 내용을 확인했다. 별도 시나리오에서 축소한 + 창에서 드래그·방향키 입력 후 저장한 값을 CLI가 복원했다. +- `document.pure`의 숫자 입력에 99를 넣자 작성한 `clamp` 규칙에 따라 저장 값 `ratio=80`을 + CLI에서 확인했다. 실제 한국어 IME 입력 시나리오는 아직 수행하지 않았다. + +기존 작성물 9개·입력 156단계의 네이티브/Wasm 상태·표시 일치, 독립 인스턴스, 실패·저장 충돌· +정의 재설치 검사는 일반 C 호스트와 ASan/UBSan C 호스트에서 통과했다. Zig 모듈은 ReleaseSafe로 +빌드했으며 ASan 대상이었다고 주장하지 않는다. 기존 Node 회귀 검사 87개도 통과했다. + +## 마이크로 비용: 범위를 섞지 않음 + +GNU `size`의 `text/data/bss`는 ELF 섹션의 합이며 RSS가 아니다. 아래 RSS·PSS는 이 호스트에서 +한 번 관찰한 프로세스 수치다. 컴파일러 자식 프로세스와 Xvfb 자체는 편집 창 프로세스 수치에 +포함하지 않는다. + +| 선택한 범위 | Linux AArch64에서 관찰한 비용 | +|---|---:| +| 공통 실행 코어 아카이브 `text/data/bss` | 5,932 / 0 / 0 B, 기존 6 KiB 예산 통과 | +| 선택 scalar 라이브러리 `text` | 33,020 B (기존 결과) | +| scalar 인스턴스 / 앱 컨트롤러 | 20,064 / 928 B | +| 실행 전용 CLI `text/data/bss` | 62,827 / 2,040 / 16 B | +| 실행 전용 CLI stripped ELF | 68,000 B; 직접 동적 의존성 `libc.so.6` | +| 실행 전용 CLI 유휴 VmRSS | 1,552 KiB | +| GTK 작성 창 `text/data/bss` | 78,964 / 2,824 / 16 B | +| GTK 작성 창 stripped ELF | 134,224 B; GTK/Pango/Cairo/GLib 동적 의존성 추가 | +| GTK 작성 창 유휴 VmRSS / PSS | 36,512 / 23,618 KiB, `GSK_RENDERER=cairo GDK_DISABLE=gl,vulkan` | +| 같은 창의 기본 렌더러 유휴 VmRSS / PSS | 173,808 / 139,710 KiB, 별도 한 번의 관찰 | + +Xvfb에서는 GPU 가속이 없으므로 마지막 두 값은 렌더러 설정에 민감했다. GTK가 문서화한 +[`GSK_RENDERER`와 `GDK_DISABLE`](https://docs.gtk.org/gtk4/running.html) 설정으로 비교했으며, +실제 GPU 환경이나 macOS 수치로 확대하지 않는다. 36 MiB조차 1.5 MiB 실행 전용 앱과 같지 +않다. 편집기 전체를 마이크로라고 주장하지 않는다. + +작성 전용 정의 교체를 공통 `app.c`에 무조건 링크했을 때 CLI `text`가 63,507 B로 늘고, +AArch64 ELF 세그먼트 정렬 경계를 넘으면서 stripped 파일이 133,536 B가 됐다. +`PURE_NATIVE_AUTHORING` 선택 빌드로 분리한 뒤 CLI는 이전의 `text=62,827 B`, stripped +68,000 B로 돌아왔다. 단순히 함수 바이트 수만 보면 놓치는 **최종 실행물의 계단식 비용**이다. +코어 자체의 코드나 외부 의존성은 증가하지 않았다. + +## 아직 성립하지 않은 주장 + +현재 보기는 기존 `.pure`의 고정된 640×360 논리 좌표를 창 크기에 맞춰 확대·축소한다. 실제 +제약 기반 레이아웃, 터치·멀티 포인터의 네이티브 검증, UI 요소의 컨테이너 이동이나 완전한 +접근성은 아직 없다. GTK 편집 호스트는 소스 적용 동안 동기적으로 컴파일하므로 긴 컴파일은 +화면 응답을 막을 수 있다. 정의 설치 후 소스 파일 쓰기가 실패하면 새 정의는 메모리에만 남고 +오류를 표시한다. 재시도는 가능하지만 정의·파일을 하나의 트랜잭션으로 확정하지 않는다. +소스 파일 저장에는 다른 편집자의 변경에 대한 조건부 갱신도 아직 없다. macOS Apple Silicon의 +AppKit/Metal 경로는 구현·검증하지 않았다. + +더 중요한 전체 체크포인트 요구인 일반 앱 표현·비동기 I/O·Pure 간 인증된 RPC·큰 실서비스· +동등 조건 성능 비교도 남았다. UI 작성 창은 런타임 전체가 아니다. 다음 판단은 실행 전용 +구성을 작은 상태로 유지하면서 **실제 앱 작업**에서 네트워크·저장·UI를 연결할 수 있는지에 +달려 있다. diff --git a/docs/research/026-secure-peer-link.md b/docs/research/026-secure-peer-link.md new file mode 100644 index 0000000..de31986 --- /dev/null +++ b/docs/research/026-secure-peer-link.md @@ -0,0 +1,105 @@ +# 026 — 선택형 Pure 피어 연결과 실제 비용 + +2026-09-24. Linux AArch64, GCC 14.2, Zig 0.15.2, OpenSSL 3.x. 첫 공개 체크포인트의 **중간 결과**다. +이 문서의 크기는 전체 용량 scalar 모듈을 사용한 첫 구현 기준이다. 이후 고정 정의 빌드의 +비용 변화는 [027](027-fixed-native-capacity.md)에 기록했다. + +## 구현한 경계 + +기존 `routed-layout.pure`를 준비한 같은 앱 인스턴스에 `pure-peer`가 입력·조회·저장·복원을 제출한다. +서버는 TCP 소켓에서 TLS 1.3을 수립한 뒤 길이가 명시된 작은 바이너리 메시지를 읽고, 기존 +`PureNativeApp`과 실행 코어를 호출한다. HTTP 문자열 헤더·메서드·URL이나 SQL 저장소를 이 +연결의 내부 형식으로 사용하지 않는다. 일반 Python `ssl` 클라이언트도 이 형식을 구현해 +상태를 읽고 작업을 제출했다. 기존 평문 HTTP 경계도 별도로 유지한다. + +```text +외부 TCP → TLS 1.3 → 선택형 peer.c/peer-wire.c → 같은 앱 작업 → 실행 코어 → scalar·저장 +``` + +이것은 **앱 선택 호스트**다. `runtime.c`, scalar 모듈, 실행 전용 `pure-native`에는 소켓·TLS +형식이 들어가지 않는다. OpenSSL을 직접 구현하지 않고 검증 가능한 라이브러리로 사용한다. +TLS에서 체인 검증과 이름 검증은 별도 확인이 필요하다. `SSL_get_verify_result()`의 성공 값만 +으로 피어 인증서를 받았다고 판단하지 않으며 실제 인증서 존재도 검사한다. +[OpenSSL 검증 결과](https://docs.openssl.org/3.5/man3/SSL_get_verify_result/), +[호스트 이름 검증](https://docs.openssl.org/3.5/man3/SSL_set1_host/). + +서버와 클라이언트 모두 CA·인증서·개인키를 지정하고 서로의 DNS 이름을 검증한다. TLS 1.3과 +ALPN `pure/1`이 맞지 않으면 종료하며 평문으로 낮추지 않는다. 0-RTT는 사용하지 않는다. +부작용 있는 작업을 0-RTT에서 안전하게 재생할 수 있다고 가정하지 않는 선택이다. +[TLS 1.3 RFC §8](https://www.rfc-editor.org/rfc/rfc8446.html#section-8), +[OpenSSL ALPN](https://docs.openssl.org/3.3/man3/SSL_CTX_set_alpn_select_cb/). + +## 이번 앱에 필요한 최소 메시지 + +TLS는 바이트 스트림이므로 각 메시지 앞에 2바이트 big-endian 길이를 둔다. 핸드셰이크 후 +서버가 `PUR1` 4바이트, 작성 정의 SHA-256 32바이트, 상태 수 1바이트, 로컬/저장 버전 각 +8바이트로 된 53바이트 hello를 보낸다. Pure 클라이언트는 정의가 다르면 작업 전에 거절한다. +일반 클라이언트는 필요에 맞게 이 식별자를 확인할 수 있다. + +| 메시지 | payload 길이 | 의미 | +|---|---:|---| +| 상태 조회 | 1 B | 현재 값과 버전. 작업 효과 없음 | +| 저장·복원 | 9 B | 동작 1 B + 기대 로컬 버전 8 B | +| 키·프레임·초기화 | 12 B | 동작·버전·입력·위상·대상 | +| 수치 입력 | 20 B | 위 필드 + IEEE binary64 값 | +| 포인터 입력 | 28 B | 위 필드 + 좌표 2개 | +| 결과 | 26 + 상태 수 × 8 B | 명시적 결과 코드·버전·오류·held·숫자 상태 | + +선택한 앱의 상태·입력 한도에 따라 요청은 최대 28 B, 결과는 최대 282 B다. 앱 내부 구조체, +포인터, 패딩을 직렬화하지 않는다. 필드 길이·바이트 순서·유한 숫자·입력 종류와 대상·상태 수를 +확인한 뒤 실행한다. 슬롯 숫자는 **현재 준비 정의의 연결 인덱스**이며 범용 안정 ABI가 아니다. +이 때문에 정의의 identity를 세션 시작에 확인한다. + +서버는 작업의 `expected revision`이 현재 로컬 버전과 다르면 상태를 바꾸지 않고 충돌을 +돌려준다. 한 연결에서 동시에 대기하는 작업은 하나로 제한한다. 응답이 유실되면 성공 여부를 +알 수 없으므로 클라이언트가 자동 재시도하지 않는다. 다시 연결해 상태·저장 버전을 읽어야 +한다. 연결이 끊어지면 해당 연결이 누른 키와 포인터 입력을 해제·취소한다. + +## 재현한 동작과 비용 + +[실행 결과](../../prototypes/workbench/results/2026-09-24-secure-peer/peer-check.json), +[빌드 식별](../../prototypes/workbench/results/2026-09-24-secure-peer/build.json), +[크기 원자료](../../prototypes/workbench/results/2026-09-24-secure-peer/size.txt). +테스트는 1일짜리 임시 CA·서버·클라이언트 인증서를 `/tmp`에 만들고 종료 시 삭제한다. +실제 비밀키를 저장소에 넣지 않는다. + +- 일반 Python TLS 클라이언트가 정의 hello와 현재 상태를 읽고, 버전 불일치 작업의 충돌과 + 상태 불변을 확인했다. 연결을 끊은 뒤 서버가 held 입력을 해제하는 것도 확인했다. +- 불명 동작·과장된 길이의 프레임, 인증서 없음·잘못된 클라이언트 이름, 잘못된 서버 이름, + ALPN 불일치를 거절했다. 인증을 끝낸 뒤 요청을 보내지 않는 클라이언트도 약 5초의 + 요청 기한 후 연결을 닫고, 다음 클라이언트가 같은 앱을 읽을 수 있었다. +- Pure 클라이언트가 녹색 상자를 잡아 이동·놓고 저장했다. 새 서버 프로세스의 첫 상태가 + `green.x=220`, `green.y=200`, 저장 버전 1, held 0임을 확인했다. +- 기존 네이티브/Wasm 9개 작성물·156개 이벤트의 일치 검사가 일반·C 호스트 ASan/UBSan에서 + 통과했고, Workbench Node 회귀 검사 87개가 통과했다. 같은 피어 통합 시나리오도 C 호스트를 + ASan/UBSan으로 계측한 실행물에서 통과했다. Zig 모듈과 OpenSSL 라이브러리까지 계측했다는 + 뜻은 아니다. + +동일한 준비 정의에 대해 `-O2 -g`로 만든 실행 파일을 비교했다. `size`의 text는 ELF 섹션 +합계이며 실제 상주 메모리와 다르다. stripped 파일 크기도 라이브러리 파일을 포함하지 않는다. + +| 범위 | 이 호스트에서 관찰한 값 | +|---|---:| +| 공통 실행 코어 아카이브 `text/data/bss`, `make check-core` | 5,932 / 0 / 0 B, 6 KiB 예산 통과 | +| `runtime.c` 단일 오브젝트 `text`, 이 명령의 `cc -O2` | 5,012 B | +| 기존 실행 전용 CLI `text`, stripped ELF | 62,827 B, 68,000 B | +| 선택형 피어 실행물 `text`, stripped ELF | 75,969 B, 133,936 B | +| 피어 실행물에 추가된 `text` | 13,142 B | +| 피어 서버 유휴 VmRSS / 인증 연결 중 VmRSS | 7,904 / 8,480 KiB, 각각 한 번 관찰 | +| 피어 실행물의 직접 동적 의존성 | OpenSSL `libssl.so.3`, `libcrypto.so.3`, libc 및 라이브러리 하위 의존성 | + +이 피어 프로세스를 “8 MiB 이하 런타임”이라 일반화하지 않는다. 인증서·TLS 연결·동적 +라이브러리·호스트 환경의 비용이 포함되며 동시 연결, 실제 부하, macOS에서는 아직 측정하지 +않았다. 실행 전용 CLI는 OpenSSL을 링크하지 않는다. 이 분리가 마이크로 비용의 현재 증거다. + +## 남은 한계와 다음 판단 + +서버는 단일 앱 인스턴스에서 한 피어씩 처리한다. DNS 조회와 동기 저장 효과는 현재 5초 +네트워크 기한에 포함되지 않는다. 원격 정의 교체, 다중 작업·스트림, 느린 소비자의 장기 예산, +권한별 역할, 인증서 교체 운영, 실제 인터넷 장애·손실·재연결 정책은 미구현이다. 5초는 현재 +프로토타입의 고정값이다. macOS Apple Silicon에서는 빌드·실행을 확인하지 못했다. + +TLS 1.3 위의 인증된 앱 작업 **정확성**을 확인했을 뿐 QUIC 수준 전송 특성이나 Kitex 수준 +성능을 보이지 않았다. 추가 성능 실험은 유휴 숫자 반복이 아니라 같은 앱 작업·보안·완료 +조건에서 실제 병목이 나타날 때 진행한다. 체크포인트에는 큰 작성물과 혼합 작업, 전체 +피크 메모리·작업별 비용, 사용자 작성·배포 경로가 더 필요하다. diff --git a/docs/research/027-fixed-native-capacity.md b/docs/research/027-fixed-native-capacity.md new file mode 100644 index 0000000..4783634 --- /dev/null +++ b/docs/research/027-fixed-native-capacity.md @@ -0,0 +1,90 @@ +# 027 — 작성 정의만큼만 예약하는 네이티브 실행물 + +2026-09-24. Linux AArch64, GCC 14.2, Zig 0.15.2. 첫 공개 체크포인트의 중간 결과다. +**기본 실행 코어의 범위를 늘리지 않고, 선택한 앱 인스턴스의 반복 비용을 줄였다.** +이 문서는 당시의 고정 용량 빌드 결과다. 이후 정의와 상태를 분리한 +[028 불변 프로그램 공유](028-shared-fixed-program.md)가 현재 CLI·피어 구조와 측정값이다. + +## 제거한 비용 + +기존 native scalar 모듈은 작은 작성물에도 상태 32개, 입력 8개, 명령 512개, 반응 64개, +포인터 대상 24개의 최대 배열을 **실행 정의와 교체 준비 공간 각각에** 예약했다. 실행 전용 CLI와 +피어는 빌드된 정의를 실시간 교체하지 않지만 인스턴스마다 20,064 B를 차지했다. 이 비용은 +동일한 작성물을 여러 독립 인스턴스로 운영할수록 반복된다. + +`prepare.mjs`가 이미 검증한 `.pure` 정의의 상태·입력·명령·반응·대상 개수를 Zig 빌드 프로필에 +함께 기록하도록 했다. `native-fixed.zig`는 기존 `engine.zig`를 해당 용량으로 빌드한다. +배열이 비어 있을 때 Zig가 인덱스 연산을 컴파일할 수 있도록 0개 항목에는 1칸만 예약한다. +언어의 상한 32/8/512/64/24는 그대로 검사한다. + +```text +.pure → 기존 컴파일러의 검증된 정의 → application.c + fixed-profile.zig + ↓ + 같은 scalar VM의 정확한 배열 용량 +``` + +고정 정의를 쓰는 `manage.py native` CLI와 `--peer`만 이 프로필을 선택한다. 작성 중에 새 +정의를 적용하는 `--desktop`과 브라우저 Wasm은 여전히 전체 용량을 가진다. 고정 실행물에서 +다른 정의로 바꾸려면 다시 빌드한다. 입력 하나의 실행 중에 크기 검사나 동적 배열 할당을 +추가하지 않았고, 새 파서나 별도 인터프리터도 만들지 않았다. 앱 계산은 여전히 기존 scalar +VM이며 AOT로 모든 연산을 기계어로 펼친 결과는 아니다. + +## 동작·한도 검증 + +[전체 용량 결과](../../prototypes/workbench/results/2026-09-24-fixed-capacity/general-parity-final.json)와 +[고정 용량 결과](../../prototypes/workbench/results/2026-09-24-fixed-capacity/fixed-parity-final.json)는 +동일한 기존 작성물 11개, 입력 170단계에 대해 네이티브 상태·출력을 Wasm 결과와 대조한다. +명령 319개인 `layout.pure`, 공통 규칙·대상별 배치를 포함한다. 두 C 호스트는 ASan/UBSan으로 +실행했고 Zig 모듈은 ReleaseSafe였다. 고정 `routed-layout.pure`에서는 용량보다 큰 상태·입력· +명령 정의가 거절되고 기존 활성 상태·버전이 남는 것도 확인했다. 브라우저/CLI 회귀 87개, +Zig 코어 37개, TLS 피어의 [인증·저장·오류 시나리오](../../prototypes/workbench/results/2026-09-24-fixed-capacity/peer-check.json)도 통과했다. + +| 작성물 | 명령 수 | 전체 용량 인스턴스 | 고정 용량 인스턴스 | +|---|---:|---:|---:| +| 비어 있는 작성물 | 0 | 20,064 B | 1,320 B | +| 카운터 | 17 | 20,064 B | 1,576 B | +| 직접 조작 | 82 | 20,064 B | 3,248 B | +| 대상별 공유 규칙 배치 | 117 | 20,064 B | 5,712 B | +| 피어와 CLI의 묶음 배치 | 117 | 20,064 B | 5,456 B | +| 명시적 세 도형 배치 | 319 | 20,064 B | 11,792 B | + +이는 `pure_scalar_bytes()`가 요구하는 인스턴스 저장 공간이다. 앱 컨트롤러, TLS·GTK, +스택·저장소·힙 관리 비용을 포함한 값이 아니다. + +반복 비용의 실제 효과를 별도로 확인했다. 같은 묶음 배치 정의로 독립 scalar 인스턴스 +1,000개를 초기화한 Linux 프로세스에서, 초기화 전후 RSS 차이의 3회 중앙값은 전체 용량 +**19,808 KiB**, 고정 용량 **5,536 KiB**였다. 둘 다 같은 실제 준비 정의를 적재했고 +각 인스턴스의 버전을 읽어 유효한 초기화를 확인했다. [원자료](../../prototypes/workbench/results/2026-09-24-fixed-capacity/instance-memory.json)와 +[재현 하네스](../../prototypes/workbench/native/instance-memory.c)를 보관했다. 이 수치는 +**scalar 인스턴스 1,000개의 메모리 확대 비용**만 비교한다. 1,000명 서비스의 전체 메모리나 +처리량·지연 개선이라는 뜻은 아니다. + +## 실행물 전체 비용 + +동일한 `routed-layout.pure`와 `-O2 -g` C 빌드의 [ELF 섹션 원자료](../../prototypes/workbench/results/2026-09-24-fixed-capacity/size.txt), +[stripped 파일 크기](../../prototypes/workbench/results/2026-09-24-fixed-capacity/stripped.txt), +[CLI 빌드 식별](../../prototypes/workbench/results/2026-09-24-fixed-capacity/fixed-cli-build.json), +[피어 빌드 식별](../../prototypes/workbench/results/2026-09-24-fixed-capacity/fixed-peer-build.json). + +| 선택한 범위 | 전체 용량 | 고정 용량 | +|---|---:|---:| +| scalar 오브젝트 `text` | 33,020 B | 18,252 B | +| CLI 실행물 `text` | 62,827 B | 48,059 B | +| CLI stripped ELF | 68,000 B | 68,000 B | +| TLS 피어 실행물 `text` | [026의 기준](026-secure-peer-link.md) 75,969 B | 61,201 B | +| TLS 피어 stripped ELF | [026의 기준](026-secure-peer-link.md) 133,936 B | 68,400 B | + +CLI의 `text`가 줄어도 stripped 파일은 이 호스트의 ELF 배치·정렬 때문에 같은 68,000 B였다. +따라서 파일 크기 개선이라고 보고하지 않는다. 반면 피어는 세그먼트 경계를 넘어 실제 +stripped 파일도 작아졌다. 동적 OpenSSL의 파일·메모리 비용은 여전히 별도이며, 이번 피어 서버 +유휴 RSS 한 번의 관찰은 7,888 KiB였다. 공통 실행 코어 아카이브의 5,932 B 예산 및 CLI가 +libc만 직접 요구하는 성질은 그대로다. + +## 다음에 제거할 중복 + +고정 용량 안에서도 각 인스턴스가 동일한 실행 정의를 **두 벌의 배열 공간에** 보관한다. +이번 변경은 최대 크기 예약을 줄였지만 준비된 명령을 인스턴스 간 공유하지는 않는다. +따라서 많은 독립 앱을 운영하는 다음 검증에서는 불변 프로그램을 공유하고 변경 가능한 상태· +입력 소유권·평가 기록만 인스턴스에 둘 수 있는지 확인해야 한다. 정의 교체의 원자성, +동시 실행의 수명, 스냅샷 호환성을 유지하면서 실제 반복 메모리를 더 줄일 수 있을 때 +구조를 채택한다. 큰 서비스 성능이나 컴파일 시간 우위는 아직 입증하지 않았다. diff --git a/docs/research/028-shared-fixed-program.md b/docs/research/028-shared-fixed-program.md new file mode 100644 index 0000000..3c48e97 --- /dev/null +++ b/docs/research/028-shared-fixed-program.md @@ -0,0 +1,79 @@ +# 028 — 고정 네이티브 앱의 불변 프로그램 공유 + +2026-09-24. Linux AArch64, Zig 0.15.2, GCC 14.2. 첫 공개 체크포인트의 중간 결과다. +마이크로 런타임의 기본 비용을 지키기 위해, **같은 정의로 실행하는 독립 인스턴스의 반복 +메모리에서 프로그램·교체 공간·선택형 캐시를 제거했다.** 코어의 기능이나 의존성을 늘리지 않았다. + +## 구조와 경계 + +[027의 고정 용량 빌드](027-fixed-native-capacity.md)는 최대 예약을 줄였지만, 한 앱의 +명령·반응·대상 정보와 교체 준비 공간을 인스턴스마다 보유했다. CLI·피어는 정의를 실행 중 +교체하지 않으므로 준비 단계가 만든 동일한 정의를 Zig 빌드의 불변 `Program`으로 한 번 +링크한다. 인스턴스에는 설치 여부, 각자의 상태·진단·평가 기록만 남긴다. 고정 빌드에서 +C의 명령·반응 배열도 다시 링크하지 않는다. + +```text +작성된 .pure → 기존 준비기 → 고정 빌드의 불변 Program (앱마다 한 벌) + ├─ scalar 인스턴스 A: 상태·진단·평가 + └─ scalar 인스턴스 B: 상태·진단·평가 +``` + +설치할 때 준비 정의의 SHA-256 identity와 상태·입력·명령·반응·대상 개수를 빌드 정의와 +대조한다. 복원 값은 모두 유한한지 확인한 뒤에야 상태·버전을 바꾼다. 여기서 identity는 +저장 상태의 **호환성 검사**이며 출처 인증이 아니다. 고정 실행물에서 다른 정의를 쓰려면 +재빌드해야 한다. 정의 staging API와 선택형 평가 캐시는 이 프로필에서 용량 오류를 반환한다. +캐시 없는 평가와 정해진 작업 예산은 계속 사용한다. 정의 교체가 필요한 GTK 작성 창과 +브라우저 Wasm은 일반 프로필을 유지한다. + +초기 버전에는 설치 전의 빈 상태를 표현하려고 2,480 B짜리 빈 `Program`이 실행 파일에 +추가로 들어갔고, 기본 포인터 초기값은 읽기 전용 영역의 동적 재배치도 만들었다. +최종 버전은 인스턴스의 설치 여부로 빈 상태를 표현한다. [실행물 의존성 검사](../../prototypes/workbench/results/2026-09-24-shared-program/dependencies.txt)에서 +CLI·피어 모두 `TEXTREL`이 없고, [심볼 검사](../../prototypes/workbench/results/2026-09-24-shared-program/symbols.txt)에서 +2,480 B `engine.shared_program` 한 벌만 남은 것을 확인했다. + +## 반복 비용과 동작 + +`routed-layout.pure`의 [최종 크기·RSS 원자료](../../prototypes/workbench/results/2026-09-24-shared-program/instance-memory.json): + +| 범위 | 일반 작성 프로필 | 앞선 고정 용량 [027](027-fixed-native-capacity.md) | 공유 고정 프로필 | +|---|---:|---:|---:| +| scalar 인스턴스 요구 저장 공간 | 20,064 B | 5,456 B | **280 B** | +| 같은 scalar 인스턴스 1,000개 초기화의 RSS 증가, 3회 중앙값 | 19,808 KiB | 5,536 KiB | **480 KiB** | + +각 인스턴스를 실제 할당·초기화·설치한 뒤 버전 합을 검사했다. RSS는 Linux의 초기화 +전후 차이이며 allocator·페이지 단위의 영향도 포함한다. 이 값은 **scalar 인스턴스 +반복 비용**이지 사용자 1,000명의 서비스 메모리·처리량이나 전체 피크가 아니다. +고정 프로그램 자체는 앱마다 정적 저장 공간 2,480 B를 한 번 사용한다. 별도의 C 앱 +컨트롤러는 이 사례에서 928 B이고, 저장·입출력·스택·TLS·GTK는 위 280 B에 없다. + +[고정 프로필](../../prototypes/workbench/results/2026-09-24-shared-program/fixed-parity.json)과 +[일반 프로필](../../prototypes/workbench/results/2026-09-24-shared-program/general-parity.json)은 +기존 작성물 11개, 입력 170단계의 상태·출력을 Wasm과 비교했다. 두 C 호스트는 ASan/UBSan, +Zig 모듈은 ReleaseSafe였다. 고정 단위 검사는 독립 상태, 설치 전 빈 상태, 잘못된 정의 +identity·개수 거부, 복원 실패의 원자성, 부호 있는 0과 reset을 확인했다. 일반 프로필의 +정의 staging·교체 검사도 통과했다. Zig 코어 37개와 브라우저/CLI 회귀 87개를 통과했다. + +## 선택한 실행물의 비용 + +같은 `routed-layout.pure`, C `-O2 -g`의 [섹션 크기](../../prototypes/workbench/results/2026-09-24-shared-program/size.txt), +[stripped 파일 크기](../../prototypes/workbench/results/2026-09-24-shared-program/stripped.txt), +[CLI 빌드 식별](../../prototypes/workbench/results/2026-09-24-shared-program/cli-build.json), +[피어 빌드 식별](../../prototypes/workbench/results/2026-09-24-shared-program/peer-build.json). + +| 선택한 범위 | 앞선 고정 용량 [027](027-fixed-native-capacity.md) | 공유 고정 프로필 | +|---|---:|---:| +| scalar 오브젝트 `text` | 18,252 B | **10,396 B** | +| CLI 실행물 `text` | 48,059 B | **37,135 B** | +| CLI stripped ELF | 68,000 B | **68,000 B** | +| TLS 피어 실행물 `text` | 61,201 B | **50,277 B** | +| TLS 피어 stripped ELF | 68,400 B | **68,400 B** | + +이 호스트의 ELF 배치·정렬 때문에 이번에는 **stripped 파일 크기가 줄지 않았다**. +CLI는 libc, 피어는 libc·OpenSSL 동적 라이브러리를 요구한다. 피어의 유휴 RSS는 +[최종 인증·복원·오류 검사](../../prototypes/workbench/results/2026-09-24-shared-program/peer-check.json)에서 +7,856 KiB였다. 공통 저장 실행 코어의 별도 `text` 예산 5,932 B는 그대로 통과했다. + +이제 작은 scalar 인스턴스만 더 줄이는 측정은 우선순위가 아니다. 첫 공개 체크포인트에는 +실제 크기의 작성·저장·원격 작업을 포함한 **앱 전체 비용과 실패 조건**이 필요하다. +이번 결과는 반복 메모리 제거와 기존 동작 유지의 증거이며 큰 서비스의 지연·처리량 우위나 +macOS 메모리 특성에 대한 증거는 아니다. diff --git a/docs/research/029-native-document-window.md b/docs/research/029-native-document-window.md new file mode 100644 index 0000000..c5084f3 --- /dev/null +++ b/docs/research/029-native-document-window.md @@ -0,0 +1,62 @@ +# 029 — 작성된 바이트 편집을 실제 네이티브 창에서 실행 + +2026-09-24. Linux AArch64, C17/GCC 14.2, GTK 4.18. 첫 공개 체크포인트의 중간 결과다. +기존 [문서 편집 연결](023-document-edits.md)을 GTK 창에 붙였다. CLI·HTTP 서버·창이 +동일한 [`editor.pure`](../../prototypes/connections/examples/editor.pure) 정의를 준비해 +동일한 실행 코어와 조건부 바이트 저장 효과를 사용한다. GTK는 **선택한 앱의 호스트 +어댑터**이며 기본 코어·CLI·서버에 들어가지 않는다. + + + +## 실제 사용 경로 + +`make -C prototypes/connections window`로 선택 빌드한다. 실행 명령은 +[사용 안내](../../prototypes/connections/README.md#네이티브-문서-창)와 같다. +창은 저장된 UTF-8 문서를 열고, GTK 텍스트 버퍼에서 편집하며, 이전 내용과 새 내용의 +공통 앞·뒤 구간을 찾아 바뀐 바이트 구간만 `store.splice`에 보낸다. 공통 구간이 UTF-8 +코드 포인트의 중간에서 끝나면 양쪽을 문자 경계로 넓힌다. 수정하지 않은 기존 본문은 +입력 메시지에 다시 싣지 않는다. 저장 구현 자체는 새 파일에 전체 범위를 다시 기록한다. + +저장 조건은 읽은 문서 버전이다. 다른 실행기가 먼저 저장했을 때 창은 충돌을 표시하고 +현재 초안을 유지한다. `Reload`는 미저장 초안이 있으면 거절하고, `Discard & reload`만 +명시적으로 초안을 버린다. 창 닫기·Ctrl+W도 미저장 변경을 확인한다. 수정 없이 저장하면 +저장 버전을 올리지 않는다. 이 창은 일반 바이트 편집기가 아니어서 유효하지 않은 UTF-8이나 +NUL이 든 저장 문서는 열지 않고 기존 화면을 유지한다. 해당 바이트는 기존 CLI가 다룬다. + +## 실제 프로세스 검증 + +[Xvfb/GTK 사용자 흐름 결과](../../prototypes/connections/results/2026-09-24-native-document-window/gtk-flow.json)와 +[ASan/UBSan으로 계측한 C 호스트 결과](../../prototypes/connections/results/2026-09-24-native-document-window/gtk-sanitized.json)는 +실제 창의 입력·저장·재시작을 검사한다. 빈 문서 생성·복원, CLI에서 창 저장 내용 확인, +HTTP 서버가 먼저 문서를 갱신한 뒤 창의 충돌·초안 보존, UTF-8에서 첫 두 바이트가 같은 +다른 글자로 교체, 변경 없는 저장, 미저장 종료 방지가 통과했다. 1 MiB 문서를 창에서 열고 +마지막 한 바이트를 바꾼 뒤 CLI로 전체 결과를 대조했다. 계측 빌드에서 GTK 라이브러리 +자체가 ASan으로 재빌드된 것은 아니다. + +기존 [연결·저장 회귀 검사](../../prototypes/connections/results/2026-09-24-native-document-window/connection-check.json)도 +통과했고, [실행 코어 예산 검사](../../prototypes/connections/results/2026-09-24-native-document-window/core-budget.json)의 +`text`는 이전과 동일한 5,932 B였다. 창용 코드는 `make all`의 필수 대상이 아니다. + +## 비용은 계층별로 기록 + +같은 호스트의 [ELF 섹션 원자료](../../prototypes/connections/results/2026-09-24-native-document-window/size.txt), +[stripped 파일 크기](../../prototypes/connections/results/2026-09-24-native-document-window/stripped.txt), +[직접 동적 의존성](../../prototypes/connections/results/2026-09-24-native-document-window/dependencies.txt), +[빌드·소스 해시](../../prototypes/connections/results/2026-09-24-native-document-window/build.json). + +| 선택 실행물 | ELF `text` | stripped 파일 | 직접 동적 의존성 | +|---|---:|---:|---| +| 문서 CLI | 35,807 B | 67,904 B | libc | +| HTTP 호환 서버 | 42,761 B | 68,000 B | libc | +| GTK 문서 창 | 43,308 B | 68,280 B | GTK·GIO·GObject·GLib·libc | + +창에서 1 MiB 문서를 연 뒤 Linux `/proc`로 본 **창 프로세스 RSS는 44,208 KiB**였다. +Xvfb·커널 캐시·다른 프로세스는 포함하지 않은 수치다. ASan 빌드의 RSS는 143,168 KiB로 +별도 검증용이며 제품 비용과 비교하지 않는다. GTK의 공유 라이브러리·렌더링·텍스트 버퍼 +비용을 무시하고 68,280 B 파일을 앱 전체 크기라 부르지 않는다. 이것은 마이크로 코어를 +실제 문서 앱에 연결한 결과지만, **전체 GTK 앱이 임베디드급 메모리라는 증거는 아니다.** + +현재 한계는 단일 문서, 동기 저장, 버전 충돌 후 수동 재적용, GTK/Linux 호스트, 바이트 +기반 저장이다. IME·문자소 단위 위치·큰 문서의 대화식 지연·macOS 실사용은 검증하지 +않았다. 이후 [030](030-authenticated-document-peer.md)에서 선택형 인증 TLS 피어까지 +**같은 문서 작업**으로 연결했다. 실제 앱의 전체 피크·실패·사용성 비용은 계속 검증해야 한다. diff --git a/docs/research/030-authenticated-document-peer.md b/docs/research/030-authenticated-document-peer.md new file mode 100644 index 0000000..1ff569d --- /dev/null +++ b/docs/research/030-authenticated-document-peer.md @@ -0,0 +1,85 @@ +# 030 — 같은 문서 작업을 인증된 Pure 피어로 연결 + +2026-09-24. Linux AArch64, C17/GCC 14.2, OpenSSL 3. 첫 공개 체크포인트의 중간 결과다. +기존 [`editor.pure`](../../prototypes/connections/examples/editor.pure)를 문서 CLI·HTTP·GTK 창과 +**같은** 준비·실행 코어에서 선택형 TLS 피어로 실행했다. 기본 빌드에는 TLS가 들어가지 않는다. + +## 실제 경로와 제거한 전송 + +`pure-document-peer`는 TLS 1.3/TCP의 상호 인증, 인증서 SAN 이름 확인, `pure-doc/1` +ALPN 뒤에 자체 이진 메시지를 사용한다. 이미 구현한 scalar 피어의 소켓·TLS 부분을 +[`tls-link.c`](../../prototypes/common/tls-link.c)로 분리해 두 피어가 재사용한다. +코어의 [`runtime.c`](../../prototypes/connections/runtime.c), 저장소, `editor.pure`는 +문서 CLI와 같다. 전송 코드는 선택 빌드의 호스트에만 있다. + +서버가 보내는 40 B hello는 `PDOC`, 버전 1, 예약 3 B, **원본 .pure 소스** SHA-256이다. +네이티브 클라이언트는 자신이 준비한 소스와 같은지 작업 전에 확인한다. 이는 현재의 +엄격한 호환성 검사이며 인증 수단은 아니다. 인증은 TLS 인증서가 담당한다. 같은 의미라도 +공백이 다른 소스는 불일치하며, 별도 외부 클라이언트는 인증을 통과하면 정의 검사 없이 +이 공개 형식을 직접 구현할 수 있다. + +요청은 14 B 고정 머리, 1~64 B 키, 선택한 본문 순서다. 머리는 작업 종류 1 B, +키 길이 1 B, 기대 버전 big-endian 8 B, 본문 길이 big-endian 4 B이다. 작업 종류의 +하위 7비트는 읽기 `1` 또는 쓰기 `2`, 최상위 비트는 **응답 본문을 소비함**을 뜻한다. +응답은 상태 1 B, 결과 버전 big-endian 8 B, **논리적 결과 길이** big-endian 4 B, +요청한 경우에만 결과 본문이다. 따라서 응답 본문을 원하지 않는 네이티브 편집 명령도 +프로그램이 산출한 길이를 받지만 그 바이트는 전송받지 않는다. 읽기 명령은 본문을 요청한다. + +쓰기 수신에서는 길이·예산·키를 확인해 실행 코어의 작업과 입력 영역을 **본문을 읽기 +전에** 예약한다. TLS 평문을 그 입력 영역에 직접 채운 뒤 한 번 공개한다. 송신에서는 +완료 결과가 대여한 범위를 TLS로 나눠 보낸 뒤 작업을 해제한다. 별도의 문서 전체 +프레임 버퍼는 없다. OpenSSL 내부 버퍼, 커널 버퍼와 저장소의 파일 재작성 비용까지 +없앤다는 뜻은 아니다. 본문이 잘리면 작업을 실행하지 않고 해제한다. + +1 MiB 삽입을 네이티브 피어에서 호출하면 프로그램의 논리적 응답 크기는 1 MiB지만 +그 호출은 응답 본문을 소비하지 않아 **TLS 앱 데이터에서 본문 1,048,576 B를 생략**한다. +같은 세션의 Python 클라이언트가 본문을 요청한 경우에는 전부 받았고, 다음 요청도 같은 +세션에서 해석됐다. 이 수치는 이 형식의 바이트 차이이며 지연·CPU 우위 측정은 아니다. + +## 재현과 비용 + +`make -C prototypes/connections document-peer`로 선택 빌드한다. 인증서 준비와 실제 +C↔C·Python↔C 시나리오는 `make -C prototypes/connections check-document-peer`로 +재현한다. [일반 빌드의 실제 TLS 결과](../../prototypes/connections/results/2026-09-24-document-peer/peer-flow.json)와 +[ASan/UBSan 결과](../../prototypes/connections/results/2026-09-24-document-peer/peer-sanitized.json)는 +상호 인증 거부, 서버 이름·ALPN 불일치, 정의 불일치, 지속 연결의 생성·읽기·충돌, +CLI와 교차 편집, 1 MiB 분할 수신·대여 응답, C 피어의 1 MiB 업로드, +잘린·잘못된·초과 본문, 유휴 기한, 재시작 후 전체 복원을 통과했다. +계측 빌드에서 OpenSSL 라이브러리 자체는 ASan으로 재빌드하지 않았다. +공통 TLS 코드로 전환한 기존 scalar 피어의 인증·기한·재접속 +[회귀](../../prototypes/connections/results/2026-09-24-document-peer/scalar-refactor.json)도 다시 통과했다. +기존 생성된 Zig 산출물에 새 C 코드를 링크해 확인했으며, 이 환경에는 Zig 0.15.2가 +없어 전체 scalar 앱 재생성 빌드는 이번 검증에서 실행하지 못했다. +문서 경로는 별도 빈 `build/checkpoint-clean` 디렉터리에서도 전체 기본 빌드와 피어를 +다시 만든 뒤 [기본 릴리스 검사](../../prototypes/connections/results/2026-09-24-document-peer/clean-release.log), +[선택형 피어 검사](../../prototypes/connections/results/2026-09-24-document-peer/clean-peer.log)를 +통과했다. + +같은 호스트의 [ELF 섹션](../../prototypes/connections/results/2026-09-24-document-peer/size.txt), +[파일 크기](../../prototypes/connections/results/2026-09-24-document-peer/file-sizes.txt), +[직접 동적 의존성](../../prototypes/connections/results/2026-09-24-document-peer/dynamic.txt), +[선택 빌드 예산 검사](../../prototypes/connections/results/2026-09-24-document-peer/peer-budget.json), +[소스·실행물 해시](../../prototypes/connections/results/2026-09-24-document-peer/hashes.txt)를 +따로 남겼다. + +| 선택 구성 | 코드 섹션 `text` | 확인한 실제 비용 | +|---|---:|---| +| 실행 코어 아카이브 | 5,932 B | 기존 6 KiB 예산 검사 그대로 통과 | +| 기본 문서 CLI | 35,807 B | libc 직접 의존 | +| 기본 HTTP 호환 서버 | 42,761 B | libc 직접 의존 | +| **선택형 문서 TLS 피어** | **46,878 B** | stripped 68,312 B; libssl·libcrypto·libc 직접 의존 | + +선택형 서버 프로세스의 `/proc` RSS는 연결 전 **7,840 KiB**, 서로 다른 두 1 MiB +문서를 처리한 뒤 **9,488 KiB**였다. 여기에는 선택한 TLS 라이브러리와 호스트 비용이 +포함된다. ASan 빌드의 22,592/31,632 KiB는 검증 전용이다. 기본 코어가 작다는 +사실을 이 전체 수치 대신 내세우지 않는다. UI는 별도 선택 구성이고 이전 GTK 문서 +창의 44 MiB RSS도 여전히 남아 있다. + +## 아직 해결하지 않은 계약 + +서버는 한 시점에 연결 하나와 작업 슬롯 하나만 처리한다. 호출 다중화, 느린 연결 사이의 +공정성, 재시도 식별자·중복 실행 억제, 키별 권한, 일반 앱 형식 협상, QUIC, 인터넷 환경의 +실패·부하·지연 검증은 없다. 각 요청의 TLS 읽기·쓰기에는 현재 5초 절대 기한이 있다. +응답이 끊긴 쓰기는 이미 저장됐을 수 있으므로 클라이언트는 재시도 전에 읽어 확인해야 한다. +저장소는 변경이 작아도 파일 전체를 다시 기록한다. 이 경로는 실용 문서 작업의 연결을 +검증하지만 Kitex급 성능이나 임베디드급 **전체 앱** 메모리를 입증하지 않는다. diff --git a/docs/research/031-relocatable-document-preview.md b/docs/research/031-relocatable-document-preview.md new file mode 100644 index 0000000..602f75e --- /dev/null +++ b/docs/research/031-relocatable-document-preview.md @@ -0,0 +1,52 @@ +# 031 — 독립된 문서 앱 묶음과 저장 비용 경계 + +2026-09-24. Linux AArch64 첫 공개 체크포인트의 중간 결과다. 기존 +[`editor.pure`](../../prototypes/connections/examples/editor.pure) 문서 앱을 새 디렉터리에서 +빌드하고, 실행물·정의 파일을 하나의 선택형 묶음으로 만들었다. + +## 실제로 옮겨 실행한 묶음 + +[`package_native.py`](../../prototypes/connections/package_native.py)는 기본 CLI·HTTP, +선택형 인증 문서 피어, 요청한 경우 GTK 창을 분리된 실행물로 담는다. 기본·피어의 +코드 섹션과 직접 동적 의존성 예산을 검사한 다음, 파일 SHA-256·크기·직접 공유 +라이브러리를 `manifest.json`에 기록한다. GTK·OpenSSL 라이브러리를 자체 코드의 +크기에 합쳐 숨기지 않고, **시스템 라이브러리를 묶음에 포함하지 않았음**을 명시한다. + +새 `build/checkpoint-clean` 디렉터리에서 `make all document-peer window`와 기본·피어 +검사를 통과한 후, [Linux AArch64 시험 묶음](../../prototypes/connections/results/2026-09-24-document-peer/pure-document-linux-aarch64.tar.gz)을 +생성했다. 압축 크기는 **316,927 B**다. 동일한 입력으로 두 번 생성한 파일과 결과 +디렉터리에 복사한 파일의 [SHA-256](../../prototypes/connections/results/2026-09-24-document-peer/package-hashes.txt)은 +모두 `dc3f7339bf5aa22b11eb5ac4c8b4a547168e0e06e65c8003a545f70a31500081`이다. +이는 같은 입력의 아카이브 재현성을 확인한 것이며, 다른 도구 버전에서 실행물까지 +같아진다는 뜻은 아니다. + +[`check_package.py`](../../prototypes/connections/check_package.py)는 압축을 별도 임시 +디렉터리에 풀어 [검증 결과](../../prototypes/connections/results/2026-09-24-document-peer/package-check.json)를 +남겼다. 파일 목록·해시를 확인하고 묶음 속 CLI로 문서를 생성·복원했다. 복사한 `.pure` +정의의 `reply(saved, edit.insert)`를 `reply(saved)`로 바꾸자 실제 결과 본문 길이가 +10 B에서 0 B로 바뀌었다. 묶음 속 인증 피어와 일반 Python TLS 클라이언트가 +1 MiB 문서를 교환했고, 묶음 속 GTK 창·CLI·HTTP가 같은 문서의 변경·충돌·저장을 +다시 통과했다. 처음 Xvfb 검사에서 창이 표시되기 전 포커스 시도가 실패해, 창 포커스를 +제한된 횟수로 다시 시도하도록 검사기를 수정한 뒤 통과했다. + +이는 **Linux AArch64 호스트에서 선택한 실용 앱이 이동해 실행된다**는 증거다. +정적 실행물, macOS 묶음, 시스템 공유 라이브러리의 재배포, 소스 설치형 패키지, +서명·릴리스 라이선스까지 해결됐다는 주장은 아니다. GTK 창의 44 MiB RSS도 압축 +파일 크기로 사라지지 않는다. + +## 작은 편집의 저장 비용은 그대로 남는다 + +현재 [`store.c`](../../prototypes/connections/store.c)의 `mutate`는 크기가 변하지 않는 +교체에도 새로운 파일에 24 B 헤더, 유지할 앞부분, 삽입 바이트, 유지할 뒷부분을 순서대로 +기록하고 `fsync`한 뒤 이름을 교체한다. 따라서 1 MiB 문서의 마지막 한 바이트를 바꿀 때 +이 경로의 **사용자 공간 파일 쓰기 요청 길이 합계는 1,048,600 B**다 +(1,048,576 B 문서 + 24 B 헤더). 이는 실제 저장 장치의 물리적 쓰기량과 같다는 +주장이 아니다. 결과 수명은 기존 파일의 읽기 전용 매핑과 새 파일 교체로 분리한다. + +이 비용을 `copy_file_range` 같은 복사 호출로 바꾸는 것만으로 새 파일의 전체 본문 +기록이 사라지지는 않는다. 또 현재 읽기 계약은 단일 연속 `PureSpan`이다. 델타나 +불변 조각으로 작은 변경만 기록하려면 읽을 때 다시 연속 버퍼를 조립하거나, 소비자가 +범위·조각을 직접 받도록 결과 표현을 바꾸어야 한다는 것이 **현재 코드에서의 추론**이다. +다음 저장 실험은 버전별 읽기 안정성, 공개 전후 실패, 충돌과 전체 앱 메모리를 그대로 +유지하면서 작은 편집의 실제 쓰기량을 줄여야 한다. 지금 구현을 이미 저쓰기량 저장소로 +소개하지 않는다. diff --git a/docs/research/032-micro-native-authoring-boundary.md b/docs/research/032-micro-native-authoring-boundary.md new file mode 100644 index 0000000..a2b8e3f --- /dev/null +++ b/docs/research/032-micro-native-authoring-boundary.md @@ -0,0 +1,82 @@ +# 032 — 마이크로 기본 비용과 선택형 네이티브 작성 창 + +## 이번 결정 + +기본 배포물에는 문서 CLI와 수정 가능한 `editor.pure`만 넣는다. HTTP, 인증 문서 피어, +GTK 작성 창은 명시적으로 선택한다. 컴파일 시 포함되지 않은 호스트의 라이브러리를 +기본 실행물로 끌어오지 않는 기존 경계를 패키지에도 적용했다. 배포물에서 DWARF +디버그 정보만 제거하며 검사를 통과한 빌드 실행물은 수정하지 않는다. + +| Linux AArch64/GCC 14 구성 | 선택 실행물 | gzip 묶음 | 이번 검증 | +|---|---|---:|---| +| 기본 | `pure-edit` | 22,482 B | 다른 위치에서 풀어 CLI 편집·복원·`.pure` 수정 효과 확인 | +| HTTP 선택 | CLI, HTTP | 47,527 B | 실제 HTTP 요청이 CLI와 같은 저장 상태를 변경 | +| TLS 선택 | CLI, 인증 피어 | 49,186 B | 1 MiB 문서 전송·오류·복원 | +| 창 선택 | CLI, GTK 창 | 48,934 B | HTTP 없이 CLI와 같은 문서의 충돌·정의 적용 확인 | +| 전체 선택 | CLI, HTTP, TLS 피어, GTK 창 | 100,900 B | 같은 검사에 1 MiB TLS 문서와 실제 GTK·CLI·HTTP 교차 편집 추가 | + +[기본 묶음](../../prototypes/connections/results/2026-09-24-native-definition-authoring/pure-document-micro-linux-aarch64.tar.gz)과 +[전체 선택 묶음](../../prototypes/connections/results/2026-09-24-native-definition-authoring/pure-document-authoring-linux-aarch64.tar.gz)은 +각각 같은 입력으로 두 번 생성했을 때 SHA-256이 일치했다. +[기본 추출 검사](../../prototypes/connections/results/2026-09-24-native-definition-authoring/micro-package-check.json), +[HTTP 선택 묶음](../../prototypes/connections/results/2026-09-24-native-definition-authoring/pure-document-http-only-linux-aarch64.tar.gz) +([결과](../../prototypes/connections/results/2026-09-24-native-definition-authoring/http-only-package-check.json)), +[TLS 선택 묶음](../../prototypes/connections/results/2026-09-24-native-definition-authoring/pure-document-peer-only-linux-aarch64.tar.gz) +([결과](../../prototypes/connections/results/2026-09-24-native-definition-authoring/peer-only-package-check.json)), +[창 단독 선택 묶음과 검사](../../prototypes/connections/results/2026-09-24-native-definition-authoring/pure-document-window-only-linux-aarch64.tar.gz) +([결과](../../prototypes/connections/results/2026-09-24-native-definition-authoring/window-only-package-check.json)), +[전체 추출 검사](../../prototypes/connections/results/2026-09-24-native-definition-authoring/full-package-check.json). +패키징 직전에도 CLI의 아래 메모리 상한을 재검사해 매니페스트에 예산 식별자를 남긴다. +이 숫자는 **압축 파일 크기**다. 실행 중 RAM이나 앱 전체 비용으로 해석하지 않는다. + +## 실제 비용 경계 + +기존 [코어 검사](../../prototypes/connections/core-budget.json)는 실행 코어 오브젝트 +`text` 5,932 B / 상한 6,144 B, data·BSS 0 B를 확인한다. 기본 CLI는 링크된 `text` +35,807 B이고 직접 동적 의존성은 libc 하나다. 코어 수치에는 파서·저장소·libc·스택이 +없으므로, 이번에는 [실제 작업 RSS 검사](../../prototypes/connections/check_micro_memory.py)를 +기본 `check-release`에 추가했다. 작은 C `fork`/`wait4` 도구로 자식을 실행해 Python +부모의 pre-exec 메모리가 자식 피크에 섞이지 않게 했다. + +| CLI 작업 | 이 장치의 피크 RSS | 회귀 상한 | +|---|---:|---:| +| 1 B 새 문서 편집 | 1,056 KiB | 4,096 KiB | +| 1 MiB 새 문서 편집 | 2,080 KiB | 4,096 KiB | +| 같은 1 MiB 문서 읽기 | 1,104 KiB | 4,096 KiB | + +[측정 결과와 정확한 범위](../../prototypes/connections/results/2026-09-24-native-definition-authoring/micro-memory.json). +이 상한은 Linux AArch64의 현재 CLI·문서 부하에만 적용된다. 캐시·커널 버퍼·다른 +작업은 포함하지 않는다. 코어 6 KiB 검사나 22 KiB 압축 묶음으로 임베디드급 +전체 앱을 입증했다고 주장하지 않는다. + +선택형 GTK 창은 1 MiB 문서를 연 상태에서 **43,792 KiB RSS**였다. 창의 링크된 +`text` 48,247 B와 직접 의존성 검사는 [별도 예산](../../prototypes/connections/window-budget.json)으로 +관리하지만, GTK 공유 라이브러리와 프로세스 전체 비용은 그 코드 수치에 포함되지 +않는다. 따라서 이 창은 현재 호스트 OS에서 사용성을 검증하는 도구이지 저메모리 UI +백엔드의 성과가 아니다. [실제 창 검사](../../prototypes/connections/results/2026-09-24-native-definition-authoring/gtk-flow-final.json). +선택형 TLS 피어도 [전체 추출 검사](../../prototypes/connections/results/2026-09-24-native-definition-authoring/full-package-check.json)에서 +유휴 7,840 KiB, 1 MiB 작업 후 9,520 KiB RSS였다. 보안 전송의 이 비용을 기본 +런타임의 비용으로 숨기거나, 기본 CLI 크기로 피어 전체가 작다고 주장하지 않는다. + +## 작성 흐름과 안전성 + +문서 창의 **Definition** 패널에서 같은 `.pure` 소스를 편집하고 적용할 수 있다. 적용은 +새 정의의 구문과 문서 편집에 필요한 전체 읽기 레이아웃을 먼저 확인한다. 원본 파일이 +외부에서 바뀌었으면 덮어쓰지 않고, 저장 실패·잘못된 정의에서도 실행 중인 이전 +계획과 소스 초안을 유지한다. 정상 적용 시 파일을 일관성·내구성 옵션으로 저장하고 +현재 창의 실행 계획을 교체한다. 다른 호스트는 재시작 뒤 새 정의를 읽는다. + +실제 GTK 입력으로 응답 규칙을 바꾼 뒤 같은 창의 다음 편집 출력이 1 B에서 0 B로 +바뀌고, 새 프로세스에서도 복원됨을 확인했다. 부분 문서 읽기 정의, 잘못된 구문, +외부 수정 충돌, 쓰기 실패, 미저장 닫기를 각각 검증했다. ASan·UBSan에서도 같은 +흐름을 통과했다. [일반 실행 결과](../../prototypes/connections/results/2026-09-24-native-definition-authoring/gtk-flow-final.json), +[계측 실행 결과](../../prototypes/connections/results/2026-09-24-native-definition-authoring/gtk-sanitized-final.json), +[작성 창 화면](../../prototypes/connections/results/2026-09-24-native-definition-authoring/window-authoring.png). + +## 남은 판단 + +이 결과만으로 첫 공개 체크포인트는 끝나지 않았다. 현재 저장소는 1 B 편집에도 +문서 파일 전체를 다시 쓰므로 1 MiB 문서에서 쓰기 증폭이 크다. 큰 앱의 동시 작업, +메모리 상한, 네이티브 UI의 별도 저메모리 구현과 macOS 검증도 남았다. 다음 연구는 +코어에 서브시스템을 계속 추가하기보다 실제 문서 작업의 저장·전달 비용을 줄이고, +같은 정확성 조건에서 전체 앱 비용을 비교하는 데 집중한다. diff --git a/docs/research/033-paged-document-storage.md b/docs/research/033-paged-document-storage.md new file mode 100644 index 0000000..4e34b4d --- /dev/null +++ b/docs/research/033-paged-document-storage.md @@ -0,0 +1,92 @@ +# 033 — 연속 읽기를 유지하며 큰 문서의 쓰기량 줄이기 + +2026-09-24. 이전 [031](031-relocatable-document-preview.md)에서 확인한 문제는 +1 MiB 문서의 1 B 교체에도 현재 저장소가 새 파일에 1,048,600 B를 기록한다는 점이다. +이 결과는 Pure의 기본 저장소를 곧바로 교체한 것이 아니라, 같은 앱 인터페이스에 +붙인 **선택형 저장 경로**를 실제 CLI·HTTP·TLS·GTK 작업으로 검증한 중간 결과다. +이 보고서는 페이지 전용 바이너리와 결과 파일의 당시 상태를 기록한다. +`store-paged.c`는 이후 [034](034-hybrid-document-storage.md)의 인라인·페이지 +혼합형으로 확장돼, 현재 소스를 새로 빌드하면 이 보고서의 페이지 전용 +바이너리와 동일하지 않다. + +## 선택한 표현과 유지한 계약 + +[`store-paged.c`](../../prototypes/connections/store-paged.c)는 현재 Linux AArch64 호스트의 +16 KiB 페이지를 사용한다. 작은 불변 루트 파일에는 문서 길이·버전·페이지 참조를 +기록하고, 데이터 파일에는 새 페이지를 덧붙인다. 길이가 같은 교체에서는 영향을 +받은 페이지만 기록한 다음 새 루트를 `fsync`·`rename`·디렉터리 `fsync` 순서로 공개한다. +읽기는 필요한 페이지들을 하나의 예약된 가상 주소 범위에 매핑하므로 기존 +`PureSpan` 소비자는 여전히 연속 바이트를 본다. `MAP_FIXED` 매핑은 자신이 예약한 +범위 안에서만 사용한다. 이 호출의 정렬·교체 규칙은 [mmap(2)](https://man7.org/linux/man-pages/man2/mmap.2.html)에, +파일과 디렉터리 동기화의 구분은 [fsync(2)](https://man7.org/linux/man-pages/man2/fsync.2.html)에, +이름 교체의 원자성은 [rename(2)](https://man7.org/linux/man-pages/man2/rename.2.html)에 +기술되어 있다. + +읽기 시작 시 공유 잠금으로 루트와 데이터 파일을 함께 선택하고 매핑이 끝나면 잠금을 +놓는다. 오래된 매핑은 새 루트가 공개되고 이전 데이터 파일이 제거돼도 이전 바이트를 +유지한다. 쓰기는 기존과 같은 `.writer.lock`의 배타 잠금을 사용하므로 버전 충돌·동시 +작성 규칙을 유지한다. 큰 문서의 세그먼트가 살아 있는 데이터의 약 2배를 넘거나 +작은 문서의 세그먼트가 최소 8페이지 상한에 이르면 새 세대를 작성하고, +공개가 내구화된 뒤 낡은 세대와 크래시 후 남은 세그먼트·임시 루트를 수거한다. +삽입·삭제처럼 뒤쪽 바이트 위치가 바뀌는 작업은 아직 새 세대 전체를 기록한다. + +## 같은 실제 작업의 측정 + +[`check_write_cost.py`](../../prototypes/connections/check_write_cost.py)는 동일한 +`editor.pure` CLI로 문서를 만든 뒤 1 B를 교체하고 `strace`에서 **객체 파일로 성공한 +사용자 공간 `write` 요청의 바이트 수**를 합산한다. 저장 장치의 물리 쓰기량, 저널, +메타데이터·디렉터리 동기화 비용 또는 실행 시간을 뜻하지 않는다. + +| 작업 | 기존 불변 파일 | 선택형 페이지 저장소 | 판단 | +|---|---:|---:|---| +| 1 MiB 문서 마지막 1 B 교체 | 1,048,600 B / 258회 | 16,944 B / 3회 | 약 **61.9배 적은 요청 바이트** | +| 1 B 문서의 1 B 교체 | 25 B / 2회 | 16,440 B / 3회 | 약 **657.6배 많은 요청 바이트** | + +[원본 측정 결과](../../prototypes/connections/results/2026-09-24-paged-storage/write-cost.json). +큰 문서의 페이지 저장소 데이터 파일은 최초 1 MiB 작성 후 이 교체에서 16 KiB가 +추가됐고, 루트는 560 B였다. 작은 문서에서는 전체 페이지 기록이 압도적으로 +비싸다. **이 이유로 페이지 저장소는 기본값이 아니다.** 다음 설계는 작은 값의 +인라인 표현과 큰 값의 페이지 표현을 같은 의미론 아래에서 선택해야 한다. + +선택형 CLI의 링크된 `text`는 39,960 B로 기존 CLI의 35,807 B보다 4,153 B 늘었으나, +기존과 같은 40,960 B 상한을 통과했다. 기본 실행 코어는 바꾸지 않았다. +[코드·의존성 결과](../../prototypes/connections/results/2026-09-24-paged-storage/paged-profile.json). +1 MiB 새 문서 편집의 선택형 CLI 피크 RSS는 2,080 KiB이며 현재 4 MiB 회귀 상한을 +통과했다. 이 수치는 대규모 동시 작업이나 파일시스템 캐시를 포함하지 않는다. +[메모리 결과](../../prototypes/connections/results/2026-09-24-paged-storage/memory.json). + +## 앱과 실패 경로 검증 + +기존 문서 앱의 [CLI·HTTP 검사](../../prototypes/connections/results/2026-09-24-paged-storage/app-release.json), +[ASan·UBSan 검사](../../prototypes/connections/results/2026-09-24-paged-storage/app-asan.json), +[페이지 저장소 단위 검사](../../prototypes/connections/results/2026-09-24-paged-storage/unit.json)와 +[계측 단위 검사](../../prototypes/connections/results/2026-09-24-paged-storage/unit-asan.json)를 +통과했다. 단위 검사는 범위 읽기, 오래된 매핑, 같은 길이 교체, 세대 재작성·수거, +공개 전 크래시, 충돌, 공개 후 내구화 불확실성 및 길이 변경을 섞은 결정적 128회 +편집을 확인한다. +[GTK 문서·정의 편집](../../prototypes/connections/results/2026-09-24-paged-storage/gtk-flow.json)과 +[인증 TLS 문서 피어](../../prototypes/connections/results/2026-09-24-paged-storage/peer.json)도 +같은 저장 경로로 1 MiB 문서와 기존 충돌·복원 작업을 통과했다. GTK 1 MiB 문서 창은 +44,656 KiB RSS여서 이 저장 변경이 UI 전체 메모리 문제를 해결했다는 뜻은 아니다. + +페이지 저장 형식은 `PUREP001`이며 기존 `PUREB001`과 호환되지 않는다. 패키지는 +형식을 확인하고 별도 저장 디렉터리를 요구한다. [선택형 CLI 묶음](../../prototypes/connections/results/2026-09-24-paged-storage/pure-document-paged-micro-linux-aarch64.tar.gz) +(25,456 B)과 [전체 호스트 묶음](../../prototypes/connections/results/2026-09-24-paged-storage/pure-document-paged-authoring-linux-aarch64.tar.gz) +(111,765 B)을 각각 다른 위치에서 풀어 +[CLI](../../prototypes/connections/results/2026-09-24-paged-storage/package-micro.json)와 +[CLI·HTTP·TLS·GTK](../../prototypes/connections/results/2026-09-24-paged-storage/package-full.json)를 +재검증했다. 각 묶음은 같은 입력에서 두 번 생성해 해시 일치를 확인했다. + +## 공개 전 남은 조건 + +이 형식은 16 KiB OS 페이지를 요구하며 macOS 빌드, 기존 저장소 마이그레이션, +작은 값의 효율적인 인라인 표현이 없다. 읽기에서 페이지 참조가 흩어질수록 매핑 +호출이 늘어나는 비용과 큰 동시 작업의 최악 지연도 아직 검증하지 않았다. +쓰기 syscall 바이트 감소가 실제 장치 쓰기량·처리량 개선을 보장하지 않는다. +따라서 현재 기본 배포는 기존 저장소를 유지한다. 선택형 경로의 다음 검증은 +작은 값과 큰 값의 혼합 작업, 오래 살아 있는 읽기, 크래시 뒤 저장 공간 수거 및 +실제 앱 지연을 같은 계약 아래에서 확인하는 것이다. + +페이지 전용 결과의 바이너리 해시와 풀어서 실행 가능한 묶음은 위 결과 디렉터리에 +보존했다. 현재 소스의 후속 구현은 `make -C prototypes/connections check-hybrid`로 +검증한다. 페이지 전용 소스 빌드 명령은 현재 트리에 더 이상 제공하지 않는다. diff --git a/docs/research/034-hybrid-document-storage.md b/docs/research/034-hybrid-document-storage.md new file mode 100644 index 0000000..0dd9a5b --- /dev/null +++ b/docs/research/034-hybrid-document-storage.md @@ -0,0 +1,77 @@ +# 034 — 작은 값은 인라인, 큰 값은 페이지로 저장 + +2026-09-24. [033](033-paged-document-storage.md)의 페이지 전용 실험은 1 MiB 문서의 +1 B 수정을 줄였지만 1 B 문서의 수정 요청량을 25 B에서 16,440 B로 늘렸다. +이번에는 같은 문서 앱의 **선택형** 저장소에서 16 KiB 이하의 값은 기존 `PUREB001` +파일에 직접 담고, 그보다 큰 값만 `PUREP001` 페이지 루트로 표현한다. 실행 코어, +기본 `store.c`, 기본 패키지는 변경하지 않았다. + +[`store-paged.c`](../../prototypes/connections/store-paged.c)는 두 형식을 읽고, 결과 +길이에 따라 다음 루트 형식을 고른다. 크기 경계를 넘을 때는 이전 형식의 논리 +바이트를 읽어 새 파일을 완성한 후 공개한다. 페이지 저장 중 길이가 같은 부분 +수정은 기존처럼 변경된 페이지만 추가한다. 형식 전환과 인라인 수정에도 기존 +버전 충돌, 이전 읽기 스냅샷, 공개 전 실패와 공개 후 `PURE_UNCERTAIN` 계약을 +적용한다. [단위 검사](../../prototypes/connections/results/2026-09-24-hybrid-storage-v2/unit.json)는 +양방향 16 KiB 경계 전환, 기존 큰 `PUREB001` 파일의 첫 수정, 오래된 읽기, +크래시·충돌·혼합 편집을 포함한다. [ASan·UBSan 검사](../../prototypes/connections/results/2026-09-24-hybrid-storage-v2/unit-asan.json)도 통과했다. + +## 실제 비용과 마이크로 경계 + +같은 `editor.pure` CLI가 문서를 만들고 1 B를 바꿀 때, `strace`에서 객체 파일에 +성공한 사용자 공간 `write` 요청만 합산했다. 물리 디스크 쓰기량, 저널, +디렉터리 동기화와 지연 시간은 포함하지 않는다. + +| 작업 | 기본 불변 파일 | 선택형 하이브리드 | +|---|---:|---:| +| 1 MiB 마지막 1 B 교체 | 1,048,600 B / 258회 | 16,944 B / 3회 | +| 1 B 문서의 1 B 교체 | 25 B / 2회 | 25 B / 2회 | + +[원본 쓰기 추적](../../prototypes/connections/results/2026-09-24-hybrid-storage-v2/write-cost.json). +큰 문서의 약 61.9배 적은 요청 바이트와 작은 문서의 동일한 요청량을 확인했다. +256개의 다른 파일이 있는 저장 디렉터리에서도 작은 값의 수정은 디렉터리 +항목을 훑는 `getdents64` 호출이 없었다. +[작은 값 경로 추적](../../prototypes/connections/results/2026-09-24-hybrid-storage-v2/small-scan.json). +단, 기존 큰 `PUREB001` 문서를 처음 수정할 때는 전체를 페이지 파일로 옮겨 +써야 한다. 그 한 번의 전환 비용과 이후 수정의 총 지연·처리량은 아직 측정하지 +않았으므로 더 빠른 저장소라는 결론은 내리지 않는다. + +코드 비용도 늘었다. 같은 Linux AArch64/GCC 14 `-O2` 빌드에서 기본 CLI의 +링크된 `text`는 35,807 B, 하이브리드 CLI는 **41,064 B**로 5,257 B 더 크다. +페이지 전용 실험의 39,960 B보다도 1,104 B 늘었다. 기존 선택형 페이지 +예산인 40,960 B를 104 B 초과했다. 그래서 실패한 예산을 자동으로 통과시킨 +것이 아니라 [별도의 42 KiB 연구 상한](../../prototypes/connections/hybrid-budget.json)을 +명시했다. [코드·직접 의존성 결과](../../prototypes/connections/results/2026-09-24-hybrid-storage-v2/profile.json)는 +CLI와 HTTP 모두 libc만 직접 필요함을 확인한다. 1 MiB 새 문서 편집 CLI의 +피크 RSS는 **2,080 KiB**이며 기존 4 MiB 회귀 상한을 통과했다. +[프로세스 메모리 결과](../../prototypes/connections/results/2026-09-24-hybrid-storage-v2/memory.json). +이 상한은 파일시스템 캐시, 동시 작업, 선택형 UI와 TLS를 포함하지 않는다. + +## 앱과 배포 경계 + +[CLI·HTTP 전체 검사](../../prototypes/connections/results/2026-09-24-hybrid-storage-v2/app-release.json), +[앱 ASan·UBSan 검사](../../prototypes/connections/results/2026-09-24-hybrid-storage-v2/app-asan.json), +[인증 TLS 문서 피어](../../prototypes/connections/results/2026-09-24-hybrid-storage-v2/peer.json), +[GTK 문서·정의 편집](../../prototypes/connections/results/2026-09-24-hybrid-storage-v2/gtk-flow.json)을 +같은 저장 경로로 통과했다. GTK 큰 문서 창은 43,632 KiB RSS로, 이 실험이 +선택형 UI의 메모리 문제를 해결하지는 않는다. + +[CLI만 든 선택형 묶음](../../prototypes/connections/results/2026-09-24-hybrid-storage-v2/pure-document-hybrid-micro-linux-aarch64.tar.gz)은 +26,045 B이고, [HTTP·TLS·GTK까지 선택한 묶음](../../prototypes/connections/results/2026-09-24-hybrid-storage-v2/pure-document-hybrid-authoring-linux-aarch64.tar.gz)은 +114,025 B다. 각각 다른 디렉터리에 풀어 +[CLI와 양쪽 저장 형식](../../prototypes/connections/results/2026-09-24-hybrid-storage-v2/package-micro.json), +[전체 앱 경로](../../prototypes/connections/results/2026-09-24-hybrid-storage-v2/package-full.json)를 +다시 확인했다. 시스템 공유 라이브러리는 묶음에 포함되지 않는다. + +작은 기존 `PUREB001` 파일은 그대로 사용할 수 있고, 큰 기존 파일도 읽고 +수정하면서 `PUREP001`로 전환할 수 있다. **기본 저장기는 전환된 큰 파일을 +읽을 수 없다.** 따라서 이 선택형 묶음에는 별도 저장 디렉터리를 사용해야 +한다. 16 KiB OS 페이지를 요구하며 macOS는 검증하지 않았다. 큰 동시 작업, +세대 수거의 최악 지연, 실제 장치 쓰기량 및 운영 중 되돌리기 절차는 남아 있다. +작은 값만 계속 수정하는 경로에서는 디렉터리 스캔을 피하므로, 크래시가 남긴 +임시 루트 파일도 다음 페이지 관련 작업 전까지 남을 수 있다. +이 비용과 제한 때문에 기본 마이크로 런타임에 포함하지 않는다. + +재현 명령은 `make -C prototypes/connections check-hybrid`와 +`python3 prototypes/connections/check_write_cost.py --flat +prototypes/connections/build/pure-edit --candidate +prototypes/connections/build/hybrid/pure-edit --candidate-mode hybrid`다. diff --git a/docs/research/035-native-multi-document-window.md b/docs/research/035-native-multi-document-window.md new file mode 100644 index 0000000..3aa910f --- /dev/null +++ b/docs/research/035-native-multi-document-window.md @@ -0,0 +1,66 @@ +# 035 — 한 네이티브 창에서 여러 문서를 선택하기 + +2026-09-24. 첫 공개 체크포인트의 문서 창은 CLI·HTTP·인증 피어와 같은 +`editor.pure`를 실행했지만, 시작 인자로 받은 문서 키 하나에 묶여 있었다. +다른 작업물을 보려면 창을 새로 시작해야 했으므로 “작업물을 선택하고 편집하는 +애플리케이션”의 사용 경험이 부족했다. + +선택형 [`edit-window.c`](../../prototypes/connections/edit-window.c)에 문서 키 입력과 +**Open · Ctrl+O**를 추가했다. 유효한 키를 열면 같은 `PureRuntime`과 작성된 +`editor.pure` 경로로 읽는다. 없는 키는 빈 새 문서로 열고, 있으면 해당 버전과 +본문을 채택한다. 읽기 결과를 확인하고 텍스트로 복사한 **뒤에만** 현재 키· +기준 본문·버전을 바꾼다. 따라서 잘못된 키, 읽기 실패, NUL/잘못된 UTF-8이 +든 객체는 현재 문서를 대체하지 않는다. 문서 또는 `.pure` 정의의 초안이 +수정된 상태라면 전환을 막아 둘 다 남긴다. 성공한 전환은 이전 문서의 출력 +표시를 지우고 새 문서의 제목·상태를 갱신한다. + +[실제 GTK 검사](../../prototypes/connections/results/2026-09-24-multi-document-window/gtk-flow-repeated.json)는 +한 창에서 `first → second → third`를 열고 각 문서를 저장·복원한다. +두 번째 문서의 미저장 본문, 수정 중인 정의 초안, 잘못된 키, 바이너리 객체에서 +현재 문서가 유지되는지 확인했다. 기존 외부 HTTP 충돌, 1 MiB 편집, 정의 적용· +실패·재시작 검사는 그대로 통과했다. +[ASan·UBSan 창 검사](../../prototypes/connections/results/2026-09-24-multi-document-window/gtk-asan-repeated.json)와 +[선택형 혼합 저장소의 같은 UI 경로](../../prototypes/connections/results/2026-09-24-multi-document-window/hybrid-gtk-flow.json)도 확인했다. + +## 비용과 배포 + +작업 접수·메모리 수명·효과 실행의 공통 코어는 바꾸지 않았다. 기본 CLI도 +그대로다. 선택형 GTK 창의 링크된 `text`는 앞선 48,247 B에서 **49,882 B**로 +1,635 B 증가했고, 기존 56 KiB 상한과 직접 의존성 검사를 통과했다. +[코드·의존성 결과](../../prototypes/connections/results/2026-09-24-multi-document-window/window-profile.json). +1 MiB 문서를 연 창의 단독 실행 RSS 표본은 **43,920 KiB**였다. 그러나 같은 +창에서 두 개의 1 MiB 문서를 64회 번갈아 열자 RSS가 +**58,752 KiB**까지 증가했다. +[8회 간격 원자료](../../prototypes/connections/results/2026-09-24-multi-document-window/switch-memory-initial.json). +이 증가는 이 작업에서 측정한 프로세스 RSS다. 후속 +[메모리 분리 실험](036-gtk-memory-boundary.md)에서는 증가가 힙에 집중되고 +텍스트 뷰 갱신을 생략한 진단 빌드에서 사라졌다. 정확한 GTK 내부 객체와 +반환 시점은 아직 확인하지 못했다. GTK 문서에 따르면 `GtkTextBuffer.set_text()`는 +[실행 취소 이력에 남지 않는 교체](https://docs.gtk.org/gtk4/method.TextBuffer.set_text.html)다. +이전 버퍼를 매번 새 것으로 바꾸는 실험도 +[증가를 없애지 못해](../../prototypes/connections/results/2026-09-24-multi-document-window/switch-memory-fresh-buffer.json) +되돌렸다. `GtkTextView.set_buffer()`는 [이전 버퍼의 참조를 놓는다](https://docs.gtk.org/gtk4/method.TextView.set_buffer.html). +따라서 이 창을 저메모리 UI의 근거로 삼지 않는다. 기본 코어·CLI의 기존 +크기·메모리·동작 검사 `make check-release`는 통과했다. + +[창 선택 묶음](../../prototypes/connections/results/2026-09-24-multi-document-window/pure-document-window-final-linux-aarch64.tar.gz)은 +49,746 B다. [다른 위치에 풀어 재검사한 결과](../../prototypes/connections/results/2026-09-24-multi-document-window/package-repeated.json)에는 +문서 전환 검사도 포함된다. 패키지 매니페스트는 이 창 기능을 명시한다. +예전 창 묶음에는 해당 표시가 없으므로 현재 검사기는 그 묶음에 새 기능을 +요구하지 않는다. [보관된 창 묶음 재검사](../../prototypes/connections/results/2026-09-24-multi-document-window/legacy-package-repeated.json)가 +통과했다. + +이것은 문서 탐색의 첫 경로다. 아직 키를 직접 입력해야 하고, 목록·검색·메타데이터 +인덱스가 없다. 여러 문서의 원자적 변경도 제공하지 않는다. 현재 작성 언어는 +`get`/`put`의 단일 저장 효과에 국한되고 동기 저장 작업은 GTK 이벤트 루프를 +막을 수 있다. 이 상태를 큰 서비스용 애플리케이션 런타임이라고 부르지 않는다. +다음 구현은 위 실제 앱에서 필요한 다중 객체 탐색·갱신의 의미와 비용을 먼저 +정하고, 그 요구를 실행 모델에 통합해야 한다. 선택형 창을 확장하기 전에는 +[반복 텍스트 교체의 메모리 증가](036-gtk-memory-boundary.md)도 해결하거나 +더 작은 UI 어댑터에서 같은 사용 경험을 검증해야 한다. 단순히 버튼을 더 +붙이는 것은 그 빈칸을 채우지 못한다. + +재현 명령은 `make -C prototypes/connections window check-window-profile`과 +`xvfb-run -a python3 prototypes/connections/check_window.py --build +prototypes/connections/build --program prototypes/connections/examples/editor.pure`다. +64회 메모리 관찰에는 마지막 명령에 `--switch-rounds 64`를 추가한다. diff --git a/docs/research/036-gtk-memory-boundary.md b/docs/research/036-gtk-memory-boundary.md new file mode 100644 index 0000000..d90246d --- /dev/null +++ b/docs/research/036-gtk-memory-boundary.md @@ -0,0 +1,89 @@ +# 036 — 선택형 문서 창의 메모리 경계 + +2026-09-24. 사용자가 원하는 것은 작은 **애플리케이션 런타임**이다. GTK 창의 +프로세스 비용을 코어의 비용처럼 취급하거나, 작은 압축 묶음만으로 메모리 목표를 +달성했다고 말할 수 없다. 여기서는 [035](035-native-multi-document-window.md)의 +1 MiB 문서 반복 전환에서 나타난 증가를 분리했다. 앱 기능을 추가하지 않았다. + +Linux AArch64에서 실제 GTK 창을 Xvfb 아래 띄우고, 1 MiB 문서 두 개를 +64회 열었다. 8회마다 `/proc/<pid>/status` RSS와 `smaps`의 영역별 RSS를 +기록했다. 각각의 대조 실험은 같은 문서 앱의 저장·편집·충돌·정의 적용 +시나리오도 통과했다. RSS는 단일 실행의 관찰값이며, 실험 간 절댓값 차이를 +성능 순위로 해석하지 않는다. + +| 조건 | 8회 후 RSS | 64회 후 RSS | 힙 RSS 증가 | +|---|---:|---:|---:| +| 두 키, 서로 다른 1 MiB 본문 | 47,472 KiB | 58,896 KiB | 11,296 KiB | +| 같은 키 다시 열기 | 47,056 KiB | 47,072 KiB | 16 KiB | +| 두 키, 동일한 본문 | 47,216 KiB | 59,680 KiB | 12,464 KiB | +| 두 키, 1 MiB 전환 시 텍스트 뷰 갱신만 생략한 **진단 빌드** | 47,216 KiB | 47,216 KiB | 0 KiB | +| 두 키, 문서 버퍼 실행 취소 기능 비활성화 | 47,440 KiB | 59,008 KiB | 11,440 KiB | +| 두 키, 줄바꿈 비활성화 | 45,888 KiB | 57,440 KiB | 11,424 KiB | + +원자료는 순서대로 +[기본](../../prototypes/connections/results/2026-09-24-window-memory-analysis/smaps.json), +[같은 키](../../prototypes/connections/results/2026-09-24-window-memory-analysis/same-key.json), +[같은 본문](../../prototypes/connections/results/2026-09-24-window-memory-analysis/same-body.json), +[텍스트 갱신 생략](../../prototypes/connections/results/2026-09-24-window-memory-analysis/skip-text.json), +[실행 취소 비활성화](../../prototypes/connections/results/2026-09-24-window-memory-analysis/no-document-undo.json), +[줄바꿈 비활성화](../../prototypes/connections/results/2026-09-24-window-memory-analysis/no-wrap.json)에 있다. +텍스트 갱신 생략 빌드는 **문서를 제대로 보여주지 않으므로 제품 수정안이 아니다.** +키·버전·본문 복사·정의·저장 경로는 그대로 실행하고, 1 MiB의 다른 키를 열 때 +`gtk_text_buffer_set_text()` 호출만 건너뛰어 원인 경계를 분리했다. + +`mallinfo2`를 추가한 별도 진단 빌드에서는 glibc 할당기의 `uordblks`가 +8회 후 10,897,312 B에서 64회 후 23,427,504 B로 증가했다 +([원자료](../../prototypes/connections/results/2026-09-24-window-memory-analysis/mallinfo.json)). +따라서 관찰된 증가는 단순히 해제된 블록을 할당기가 보유하는 현상만으로 +설명되지 않는다. 반면 이 수치는 어떤 GTK 객체가 데이터를 소유하는지, +그 객체가 언제 반환되는지까지 밝히지 않는다. 종료 후 누수량과 실행 중 +힙 사용량도 같은 지표가 아니므로, 이 결과만으로 누수를 단정하지 않는다. + +GTK 문서는 [`GtkTextBuffer.set_text()`가 이전 내용을 지우고 새 내용을 넣으며 +실행 취소 불가능한 작업으로 표시된다](https://docs.gtk.org/gtk4/method.TextBuffer.set_text.html)고 +설명한다. 실행 취소와 줄바꿈을 각각 꺼도 증가량이 유지된 점과 함께 보면, +현재 위치는 **GTK 텍스트 교체 경계**다. 이전 버퍼를 새 버퍼로 바꾸는 실험도 +[035의 원자료](../../prototypes/connections/results/2026-09-24-multi-document-window/switch-memory-fresh-buffer.json)에서 +효과가 없었다. 특정 GTK 내부 캐시나 누수라는 결론은 내리지 않는다. + +재현 가능한 제품 빌드의 기준 경로는 +`make -C prototypes/connections window editor` 다음 +`xvfb-run -a python3 prototypes/connections/check_window.py --build prototypes/connections/build --program prototypes/connections/examples/editor.pure --switch-rounds 64 --diagnose-maps`다. +진단 변형은 제품 코드에서 제거했다. 텍스트 갱신 생략 변형은 +`open_document_key()`의 `gtk_text_buffer_set_text()` 앞에 +`if (!(strcmp(key, e->key) != 0 && e->baseline_size >= 1000000))`를 +놓되, 키 변경 전의 값을 비교해야 한다. 다른 변형은 창 생성 시 문서 버퍼에 +`gtk_text_buffer_set_enable_undo(buffer, FALSE)`를 호출하거나 문서 뷰의 +`GTK_WRAP_WORD_CHAR`를 `GTK_WRAP_NONE`으로 바꾼 것이다. `mallinfo2` +변형은 문서 열기 완료 직후 할당기 통계를 기록했다. + +현재 비용 경계는 분명하다. 기본 CLI의 1 MiB 작업에는 +[4 MiB 최대 RSS 검사](../../prototypes/connections/check_micro_memory.py)가 있고, +GTK·TLS는 기본 실행물에 링크되지 않는다. 선택형 GTK 창은 시작부터 약 +44 MiB RSS를 쓰고 위 반복 전환에서는 더 증가한다. 따라서 **이 창을 +마이크로 메모리 프로필이라고 부르지 않는다.** 선택형 창의 기능 검증은 +유효하지만 저메모리 UI를 증명하지 않는다. 큰 앱·서비스로 확장하기 전에 +실행 코어와 어댑터의 비용을 계속 따로 공개하고, UI에는 실제 화면·입력· +편집 기능을 유지한 상태에서 전환 후 메모리가 안정되는 구현이 필요하다. + +진단 코드를 제거한 최종 창도 [64회 전환 기능·RSS 검사](../../prototypes/connections/results/2026-09-24-window-memory-analysis/final-native-window.json)를 +통과했고, RSS는 8회 후 47,216 KiB에서 64회 후 58,528 KiB로 증가했다. +같은 상태의 `make check-release check-window-profile`에서는 코어 `text` +5,932 B, 기본 CLI의 1 MiB 편집 피크 RSS 2,080 KiB, 선택형 GTK 창의 +`text` 49,946 B가 각 회귀 상한을 통과했다. GTK 창의 코드 섹션 통과는 +전체 GTK 프로세스의 메모리 통과를 뜻하지 않는다. 이 결과를 반영해 다음 +GUI 기능을 추가하기 전에 창 메모리의 경계 또는 더 작은 네이티브 UI 어댑터를 +검증해야 한다. + +추가 대조에서 마지막 전환 후 10초간 창을 유휴 상태로 두어도 RSS는 +59,328→59,456 KiB로 줄지 않았다 +([원자료](../../prototypes/connections/results/2026-09-24-window-memory-analysis/settled-final.json)). +같은 1 MiB를 80 B 줄 대신 4,096 B 줄로 구성하면 8→64회 힙 RSS 증가가 +11,296 KiB에서 17,792 KiB로 오히려 커져, 단순한 **줄당 고정 비용** 가설도 +맞지 않았다 +([원자료](../../prototypes/connections/results/2026-09-24-window-memory-analysis/line-width-4096.json)). +전환마다 문서 `GtkTextView` 전체를 파괴·재생성한 진단 빌드 역시 힙 RSS가 +14,416→25,968 KiB로 증가했다 +([원자료](../../prototypes/connections/results/2026-09-24-window-memory-analysis/replace-view.json)). +이 세 실험은 GTK 내부 소유자를 특정하지 않는다. 뷰를 매번 재생성하는 +제품 코드는 채택하지 않았고, 작은 코어의 실행 모델 작업에 우선순위를 둔다. diff --git a/docs/research/037-build-time-prepared-runner.md b/docs/research/037-build-time-prepared-runner.md new file mode 100644 index 0000000..1a6fae4 --- /dev/null +++ b/docs/research/037-build-time-prepared-runner.md @@ -0,0 +1,59 @@ +# 037 — 작성 도구와 실행 전용 문서 앱 분리 + +2026-09-24. [036](036-gtk-memory-boundary.md)의 GTK 창은 44 MiB 이상의 +선택 비용을 지닌다. 마이크로 목표를 지키기 위해 이번에는 화면 기능을 늘리지 +않고, 기존 `.pure` 문서 앱의 **실행물에 매번 싣던 파서**를 분리했다. + +[`pure-compile`](../../prototypes/connections/compile-program.c)은 빌드 때 +기존 `pure_program_parse()`가 검증한 `PureProgram`을 C 초기화자로 출력한다. +[`pure-edit-fixed`](../../prototypes/connections/edit.c)는 생성된 상수를 +기존 `PureRuntime`과 기본 파일 저장소에 연결한다. 실행기 링크에는 +`program.c`와 컴파일 도구가 없다. 별도 `pure-edit`는 `.pure` 소스를 실행 때 +읽는 작성·실험 경로로 남긴다. 새 VM, 로더, 가변 명령 디스패치 또는 코어의 +항상 포함 기능을 추가하지 않았다. + +같은 Linux AArch64/GCC 14 `-O2` 빌드에서 기본 문서 CLI의 링크된 `text`는 +35,807 B, 고정 정의 실행기는 **19,244 B**다. 같은 `editor.pure`의 읽기, +조건부 수정, 응답과 기본 파일 저장을 수행하면서 **16,563 B(46.3%)** 줄었다. +직접 공유 라이브러리 의존성은 둘 다 libc뿐이다. +[20 KiB 실행기 코드 상한 결과](../../prototypes/connections/results/2026-09-24-fixed-program/profile-final.json). +컴파일 도구 자체의 `text`는 22,034 B이며 빌드 시 필요하다. 이를 프로젝트 +전체 비용에서 사라진 것으로 세지 않는다. + +1 MiB 문서 편집의 피크 RSS는 고정 실행기 **2,112 KiB**였다 +([4 MiB 회귀 상한 결과](../../prototypes/connections/results/2026-09-24-fixed-program/memory-verified.json)). +기존 소스 실행기의 같은 측정은 2,080 KiB였다. 따라서 이 결과는 **코드 +크기 감소**를 입증하지만 RAM 감소를 입증하지 않는다. 실행기 패키지는 +14,385 B 압축 파일이다 +([이동 후 실제 편집·복원 검사](../../prototypes/connections/results/2026-09-24-fixed-program/package-final.json)). +기존 22,482 B 소스 작성 패키지와는 포함 파일 및 실행 중 정의 변경 능력이 +다르므로 패키지 크기를 동등 기능의 성능 비교로 쓰지 않는다. 이 고정 패키지는 +`editor.pure` 파일과 파서·컴파일러를 포함하지 않는다. + +[동작 일치 검사](../../prototypes/connections/results/2026-09-24-fixed-program/parity-release.json)는 +소스 실행기와 고정 실행기를 서로 다른 저장 디렉터리에서 같은 작업으로 +비교했다. 문서 생성·수정·오래된 버전 충돌·잘못된 범위·1 MiB 복원뿐 아니라 +`selection.pure`의 동적 입력 범위, `excerpt.pure`의 중첩 읽기·필요 길이 +검사까지 결과 코드·본문·메타데이터가 같았다. 잘못된 소스의 컴파일은 기존 +생성 파일을 유지했고, 입력 파일을 출력 대상으로 지정하는 것도 거절했다. +같은 빌드 디렉터리에서 작성 정의를 +`editor → selection → editor`로 바꾸면 실행물이 각각 다시 빌드됐다. +`nm`에서도 고정 실행기의 파서 심볼이 없음을 확인했다. 기존 +`make check-release`, 새로운 `make check-fixed-profile`, 이동한 묶음의 +검사가 모두 통과했다. GTK 창의 기존 기능 검사도 통과했다. + +이 경로는 현재 **C 소스 초기화자**를 생성해 같은 대상의 C 컴파일러로 +빌드한다. 안정적인 이식 바이트코드 형식이나 대상 독립 ABI는 아니다. +사용자가 실행 중 `.pure` 정의를 바꾸려면 기존 소스 실행기·작성 창이 +필요하고, 고정 실행기는 다시 빌드해야 한다. 현재 작성 언어 자체도 +`get`/`put` 한 저장 효과 범위다. 따라서 코드 크기 절감이 대규모 앱 표현, +네이티브 UI 메모리, macOS 검증을 대신하지 않는다. 다음 구현은 더 큰 +앱을 작성 가능하게 하면서도 이 준비·실행 분리와 크기 상한이 유지되는지 +확인해야 한다. + +재현 명령: `make -C prototypes/connections fixed-editor check-fixed-profile`, +`make -C prototypes/connections BUILD=build/fixed-selection +FIXED_PROGRAM=examples/selection.pure fixed-editor`, +`make -C prototypes/connections BUILD=build/fixed-excerpt +FIXED_PROGRAM=examples/excerpt.pure fixed-editor`, +`python3 prototypes/connections/check_fixed_program.py`. diff --git a/docs/research/038-fixed-http-document-host.md b/docs/research/038-fixed-http-document-host.md new file mode 100644 index 0000000..5eeeaaf --- /dev/null +++ b/docs/research/038-fixed-http-document-host.md @@ -0,0 +1,51 @@ +# 038 — 준비된 한 앱을 CLI와 HTTP 경계에서 실행하기 + +2026-09-24. [037](037-build-time-prepared-runner.md)의 빌드 시 준비 경로를 +같은 문서 앱의 HTTP 호스트까지 확장했다. `pure-connections-fixed`는 +`editor.pure`에서 준비한 정적 `PureProgram`을 기존 실행 코어·파일 저장소와 +연결한다. 외부 HTTP 형식은 어댑터에서 처리하고, 실행 코어에 HTTP 타입이나 +파서를 넣지 않았다. 소스 작성용 `pure-connections`는 그대로 남는다. + +| Linux AArch64/GCC 14 `-O2` 구성 | 링크된 `text` | 직접 공유 의존성 | +|---|---:|---| +| 소스 정의 HTTP 호스트 | 43,009 B | libc | +| 고정 정의 HTTP 호스트 | **25,228 B** | libc | + +같은 문서 기능의 호스트 실행물에서 파서 분리로 **17,781 B(41.3%)**를 +줄였다. 실행기 구조체도 41,528→40,848 B로 680 B 작아졌는데, 이는 +소스 호스트에 있던 `PureProgram` 인스턴스를 정적 상수로 공유한 결과다. +[두 프로필의 코드·직접 의존성](../../prototypes/connections/results/2026-09-24-fixed-http/source-profile.json), +[고정 호스트의 26 KiB 회귀 상한](../../prototypes/connections/results/2026-09-24-fixed-http/fixed-profile.json). +압축 크기나 코드 섹션만으로 프로세스 메모리·네트워크 성능 향상을 주장하지 않는다. + +[실제 HTTP·CLI 교차 검사](../../prototypes/connections/results/2026-09-24-fixed-http/verified.json)에서는 +고정 HTTP로 만든 문서를 소스 HTTP가 읽고, 소스 HTTP의 수정을 고정 HTTP가 +읽었다. 오래된 버전과 잘못된 수정 범위를 양쪽에서 거절했고, 고정 HTTP에서 +1 MiB 문서를 생성한 뒤 소스 HTTP·고정 CLI가 같은 바이트를 읽었다. +CLI에서 끝 1 B를 수정한 뒤 양쪽 HTTP가 새 버전을 복원했다. 고정 HTTP +실행물에는 `pure_program_parse`·`pure_program_load` 심볼이 없다. + +같은 검사에서 느린 클라이언트가 1 MiB 응답을 한 바이트만 받은 동안, +1 MiB 보유 예산 아래의 다른 읽기는 거절됐다. 첫 응답을 끝까지 소비하자 +다음 요청이 성공했고, 종료 시 보유량과 활성 작업은 모두 0이었다. +이것은 응답 버퍼의 수명·예산·회수 검증이며 Kitex급 처리량을 증명하지 않는다. +한 번 실행의 프로세스 RSS 표본은 고정 HTTP 유휴 1,520 KiB, 소스 HTTP +유휴 1,552 KiB였다. 1 MiB 문서 작업 후 각각 1,648/1,680 KiB였다. +고정 HTTP의 이 작업 중 최고 RSS는 2,128 KiB로 **4 MiB 회귀 상한**을 +통과했다. 소스 HTTP와의 작은 RSS 차이를 안정적인 RAM 이점으로 단정하지 +않는다. 이 상한은 단일 1 MiB 작업과 위 느린 수신자 흐름에 적용되며, +동시 다중 클라이언트 전체 피크의 보증이 아니다. + +[CLI+HTTP 묶음](../../prototypes/connections/results/2026-09-24-fixed-http/pure-document-fixed-http-linux-aarch64.tar.gz)은 +31,733 B이고 libc 외 공유 라이브러리를 묶지 않는다. +[다른 디렉터리에서 해제·동작 검사](../../prototypes/connections/results/2026-09-24-fixed-http/package.json)는 +패키지의 두 호스트가 같은 저장 파일을 수정·복원함을 확인했다. 소스 정의나 +컴파일러가 들어 있지 않아 실행 중 앱을 다시 작성할 수 없다. 기존 소스·HTTP +패키지도 변경된 검사기로 재검증했다. 내부 Pure 피어의 TLS·정의 일치, +macOS 빌드, 다중 앱·대규모 서비스는 이 결과의 범위 밖이다. + +재현 명령은 `make -C prototypes/connections fixed-editor fixed-server +check-fixed-profile check-fixed-server`, +`python3 prototypes/connections/package_native.py --fixed --with-http --output +/tmp/pure-fixed-http.tar.gz`, `python3 prototypes/connections/check_package.py +/tmp/pure-fixed-http.tar.gz`다. diff --git a/docs/research/039-idle-http-wakeups.md b/docs/research/039-idle-http-wakeups.md new file mode 100644 index 0000000..f958aec --- /dev/null +++ b/docs/research/039-idle-http-wakeups.md @@ -0,0 +1,50 @@ +# 039 — 유휴 HTTP 호스트의 주기적 깨움 제거 + +후속 [041](041-posix-idle-signal-wakeup.md)에서 Linux 전용 `ppoll` 경로를 +POSIX 신호 알림 파이프로 교체했다. 아래 수치와 묶음은 그 전 단계의 기록이다. + +2026-09-24. [038](038-fixed-http-document-host.md)의 문서 HTTP 호스트는 +연결이 없어도 `poll(..., 50)`을 반복했다. 마이크로 런타임에서는 상시 실행 +서비스의 유휴 CPU 깨움도 비용이다. 이번 변경은 사용 중인 연결들의 가장 +이른 절대 기한까지 기다리고, 연결이 없으면 새 접속이나 종료 신호가 올 때까지 +블로킹한다. 전용 타이머 스레드, 실행 코어 타입, 추가 큐를 만들지 않았다. +종료 신호가 대기 직전에 도착하는 경합을 막기 위해 Linux 호스트는 +`SIGINT`·`SIGTERM`을 평소에 보류하고 `ppoll`이 대기할 때 원자적으로 +해제한다. 신호는 대기를 깨우며, 다음 루프에서 종료 상태를 확인한다. + +[시스템 호출 비교](../../prototypes/connections/results/2026-09-24-signal-safe-idle/reproducible-comparison.json)는 +같은 Linux AArch64 호스트에서 연결 없는 1.25초를 관찰했다. 보관된 +[이전 고정 HTTP 묶음](../../prototypes/connections/results/2026-09-24-fixed-http/pure-document-fixed-http-linux-aarch64.tar.gz)의 +호스트는 **50ms 제한의 `ppoll` 25회**, 새 호스트는 **제한 시간 없는 대기 +1회**였다. 비교기는 보관된 패키지의 실행물을 직접 추출해 추적하므로 +임시 빌드나 기억에 의존하지 않는다. 이는 시스템 호출·깨움 횟수에 대한 +실측이며 배터리 소모량이나 처리량 비교는 아니다. + +활성 연결이 있으면 각 연결의 절대 기한 중 가장 가까운 값으로 대기 시간을 +정한다. 이미 지난 기한은 즉시 처리한다. 기존 소스 정의 HTTP 검사 `make check-release`는 +1.2초 기한의 느린 수신자·여덟 개 불완전한 헤더 연결을 회수하는 경로를 +포함해 통과했다. [고정 정의 HTTP 회귀 검사](../../prototypes/connections/results/2026-09-24-signal-safe-idle/fixed-server-regression.json)도 +1 MiB 문서 교차 송수신, 느린 수신자의 예산 점유·회수와 4 MiB 프로세스 +피크 상한을 통과했다. 유휴 블로킹 중 `SIGTERM`이 대기를 깨우고 정상 +종료하는 것도 비교 추적에서 확인했다. + +링크된 `text`는 소스 HTTP 43,009→**43,451 B**, 고정 HTTP +25,228→**25,790 B**로 증가했다. 앞선 대기 변경만으로는 각각 64 B가 +늘었지만, 종료 신호 경합까지 막은 최종 비용은 각각 442 B와 562 B다. +실행 코어 5,932 B와 기본 CLI는 그대로다. +[소스 호스트 크기](../../prototypes/connections/results/2026-09-24-signal-safe-idle/source-profile.json)와 +[고정 호스트 크기](../../prototypes/connections/results/2026-09-24-signal-safe-idle/fixed-profile.json)는 +각각 기존 상한을 통과했다. [새 CLI+HTTP 묶음](../../prototypes/connections/results/2026-09-24-signal-safe-idle/pure-document-fixed-http-linux-aarch64.tar.gz)은 +32,023 B이고 [다른 위치에서의 편집·복원](../../prototypes/connections/results/2026-09-24-signal-safe-idle/package.json)을 +통과했다. 패키지에는 시스템 libc가 포함되지 않는다. + +이 결과는 **Linux 호스트의 유휴 폴링 제거**다. `ppoll` 신호 마스크는 +Linux 구현이므로 다른 플랫폼의 대기·종료 경로, 실제 전력, 높은 동시성의 지연·처리량과 큰 앱 표현은 +아직 검증하지 않았다. 서비스가 연결을 처리할 때 저장 효과는 여전히 +동기식이므로 이 변경으로 그것이 비동기가 되지는 않는다. + +재현 명령은 +`python3 prototypes/connections/check_idle_wakes.py --before +prototypes/connections/results/2026-09-24-fixed-http/pure-document-fixed-http-linux-aarch64.tar.gz +--after prototypes/connections/build/pure-connections-fixed`, +`make -C prototypes/connections check-release check-fixed-profile check-fixed-server`다. diff --git a/docs/research/040-document-service-capacity.md b/docs/research/040-document-service-capacity.md new file mode 100644 index 0000000..4016089 --- /dev/null +++ b/docs/research/040-document-service-capacity.md @@ -0,0 +1,48 @@ +# 040 — 한 문서 앱의 다중 객체·동시 요청 용량 확인 + +2026-09-24. 작은 코어와 한 문서 예제만으로 서비스 규모의 메모리 특성을 +판단할 수 없다. 기존 `editor.pure`를 그대로 사용하고, 소스 정의 HTTP 호스트와 +빌드 시 준비한 고정 정의 HTTP 호스트에 동일한 앱 작업을 실행했다. 새 런타임 +타입이나 전용 저장 형식을 추가하지 않았다. + +[재현 검사](../../prototypes/connections/check_service_scale.py)는 각 호스트에 +**8개 클라이언트**를 연결해 **1,024개 키**를 분담한다. 키마다 8,192 B 문서를 +생성하고, 읽고, 마지막 1 B를 조건부 수정하고, 새 버전을 다시 읽는다. +각 응답의 상태·버전·본문을 검증하고, 두 호스트가 만든 모든 최종 문서의 +해시를 비교한다. 호스트 종료 후에는 별도 CLI 프로세스로 네 문서를 읽어 +저장된 내용과 버전을 확인한다. 한 호스트당 **4,096 HTTP 작업**과 2,048개 저장 효과, +2,048개 읽기 효과다. 키별 작업은 순차적이며, 서로 다른 키 사이에 동시성이 +있다. 이전 검사에서 같은 키의 충돌과 느린 수신자는 별도로 검증했다. + +[현재 호스트의 실행 결과](../../prototypes/connections/results/2026-09-24-posix-idle/service-scale.json): + +| 한 호스트 기준 | 고정 정의 | 소스 정의 | +|---|---:|---:| +| 최종 저장 객체 | 1,024 | 1,024 | +| 저장 파일 총량 | 8,413,184 B | 8,413,184 B | +| 서버 유휴 RSS | 1,504 KiB | 1,552 KiB | +| 이 작업의 피크 RSS | **1,680 KiB** | **1,712 KiB** | +| 최대 동시 연결 | 8 | 8 | +| 최대 보유 payload | 49,185 B | 49,168 B | +| 종료 시 보유 payload | 0 B | 0 B | +| 거절·기한 만료 | 0 | 0 | + +두 호스트의 최종 문서 해시는 같다. 1 MiB 서버 payload 예산과 4 MiB +프로세스 피크 RSS 회귀 상한을 통과했다. 응답으로 빌려 보낸 논리적 본문은 +호스트당 25,166,848 B였고, 요청 헤더와 같은 첫 수신에 실린 본문을 입력 +버퍼로 옮긴 바이트는 이 실행에서 고정 정의 326,623 B, 소스 정의 +366,236 B였다. [앞선 동일 작업](../../prototypes/connections/results/2026-09-24-signal-safe-idle/service-scale-restored.json)에서는 +각각 696,751 B와 410,074 B였다. +TCP 분할·스케줄링에 따라 달라지는 값이므로 두 호스트의 우열을 뜻하지 +않지만, 복사 제거가 아직 완전하지 않다는 근거다. 빌린 출력 바이트는 실제 +네트워크·커널 복사량이나 고유 물리 메모리량을 뜻하지 않는다. + +이 검사는 **실제 문서 앱의 다중 객체·8연결 정확성 및 자원 상한**을 보여준다. +Python 클라이언트가 만든 요청의 실행 시간을 서버 처리량으로 취급하지 +않는다. 저장 파일과 페이지 캐시, 파일 디스크립터, OS 네트워크 버퍼, +클라이언트 메모리는 서버 RSS에 포함되지 않는다. 1,024개 파일을 쓰는 +운영·복구 비용, 다수의 1 MiB 동시 응답, 장시간 부하, 여러 Pure 프로세스, +실서비스 동등 비교와 macOS는 아직 검증하지 않았다. 한 바이트 수정에도 +기본 저장소가 문서 전체를 다시 쓰는 비용 역시 남아 있다. + +재현 명령은 `make -C prototypes/connections check-service-scale`이다. diff --git a/docs/research/041-posix-idle-signal-wakeup.md b/docs/research/041-posix-idle-signal-wakeup.md new file mode 100644 index 0000000..5f7ba40 --- /dev/null +++ b/docs/research/041-posix-idle-signal-wakeup.md @@ -0,0 +1,37 @@ +# 041 — 유휴 HTTP 호스트의 POSIX 종료 경로 + +2026-09-24. [039](039-idle-http-wakeups.md)는 50ms 주기 깨움을 없앴지만 +Linux `ppoll`에 의존했다. `poll` 대기 직전에 종료 신호가 도착하면 신호 +플래그만으로는 무기한 대기 경합을 막을 수 없다. 이번 변경은 HTTP 호스트에 +작은 비차단 신호 알림 파이프를 둔다. `SIGINT`·`SIGTERM` 처리기는 종료 +플래그를 설정하고 파이프에 한 바이트를 쓴다. `poll`은 그 읽기 끝을 +클라이언트 소켓과 함께 감시한다. 신호가 `poll` 전후 어느 때 도착해도 +다음 대기가 깨어날 준비를 한다. 대기 전용 스레드나 실행 코어 타입은 없다. + +Apple의 [`poll(2)` 매뉴얼](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/poll.2.html)과 +[`sigaction(2)` 매뉴얼](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/sigaction.2.html)은 +이 경계에서 쓰는 API를 문서화하고, 후자는 `write()`를 신호 처리기에서 +사용 가능한 함수로 열거한다. 여기서 검증한 것은 **Linux AArch64 실행**이다. +macOS SDK 컴파일·실행과 macOS 저장·패키지 경로는 아직 검증하지 않았다. + +[시스템 호출 추적](../../prototypes/connections/results/2026-09-24-posix-idle/reproducible-comparison.json)에서 +유휴 1.25초 동안 이전 호스트의 50ms 제한 대기 25회가 현재 호스트의 +무기한 대기 1회로 바뀌었다. `SIGTERM` 이후 프로세스는 정상 종료했다. +[HTTP 회귀 검사](../../prototypes/connections/results/2026-09-24-posix-idle/fixed-server-regression.json)는 +1 MiB 문서·느린 수신자·예산 회수를 통과했다. +[1,024개 문서·8개 클라이언트 검사](../../prototypes/connections/results/2026-09-24-posix-idle/service-scale.json)도 +고정·소스 호스트 각각 4,096 HTTP 작업, 같은 최종 문서 해시, 별도 CLI +복원, 종료 시 보유 0 B를 확인했다. 이 검사는 처리량 비교가 아니다. + +Linux AArch64/GCC 14에서 실행 코어 `text` 5,932 B와 고정 CLI 19,244 B는 +그대로다. [소스 HTTP 호스트](../../prototypes/connections/results/2026-09-24-posix-idle/source-profile.json)의 +`text`는 43,451→43,778 B, [고정 HTTP 호스트](../../prototypes/connections/results/2026-09-24-posix-idle/fixed-profile.json)는 +25,790→26,261 B다. 호스트마다 파이프 파일 디스크립터 2개를 더 쓴다. +[CLI+HTTP 묶음](../../prototypes/connections/results/2026-09-24-posix-idle/pure-document-fixed-http-linux-aarch64.tar.gz)은 +32,346 B이며 [다른 위치에서 실제 편집·복원](../../prototypes/connections/results/2026-09-24-posix-idle/package.json)을 +통과했다. 묶음 크기는 libc나 프로세스 메모리를 포함하지 않는다. + +`ppoll` 제거만으로 macOS 지원이 완성되지는 않는다. 현재 저장소, +컴파일러, 창, TLS 경계와 패키지도 macOS에서 각각 빌드·검증해야 한다. +마이크로 기준도 실행 코어만이 아니라 선택한 앱의 전체 메모리와 파일 +디스크립터를 포함해 판단해야 한다. diff --git a/docs/research/042-micro-native-terminal-editor.md b/docs/research/042-micro-native-terminal-editor.md new file mode 100644 index 0000000..5adb5a2 --- /dev/null +++ b/docs/research/042-micro-native-terminal-editor.md @@ -0,0 +1,44 @@ +# 042 — 같은 문서 앱의 저메모리 네이티브 터미널 편집기 + +2026-09-24. 기존 GTK 문서 창은 실제 편집 경로를 증명했지만 유휴부터 약 +44 MiB RSS를 사용한다. Pure의 작은 실행 구성을 실제 입력 인터페이스에서도 +확인하기 위해 **선택형 POSIX 터미널 편집기** `pure-edit-tui-fixed`를 만들었다. +기존 `editor.pure`를 빌드 시 준비한 실행 계획, `runtime.c`의 작업·예산·수명, +`store.c`의 조건부 저장 효과를 CLI·HTTP와 공유한다. 코어에 터미널 타입, +렌더러 또는 전용 UI 스레드를 넣지 않았다. + +편집기는 터미널의 크기 변경을 처리하고, 화살표로 커서를 이동하며, 한국어 +UTF-8 문서를 입력·저장한다. Ctrl+O로 키를 열고, Ctrl+S로 저장하며, +Ctrl+R은 다시 읽고 Ctrl+Q는 종료한다. 미저장 초안을 버리는 명령은 두 번 +입력해야 한다. 외부 실행기가 먼저 같은 문서를 수정하면 저장의 버전 충돌을 +보여주고 초안은 유지한다. 편집 중에는 갭 버퍼를 사용해 커서 주변 입력을 +파일 전체 이동 없이 처리한다. 저장 직전에 현재 파일 버전을 읽고 변경 +구간만 작성된 `splice` 입력에 담는다. 저장 대상 파일이 크면 기본 저장소가 +파일 전체를 다시 쓰는 비용은 여전히 남는다. + +[실제 PTY 검사](../../prototypes/connections/results/2026-09-24-fixed-tui-final/pty-flow.json)는 +커서 위치의 문서 생성·저장, 두 키 전환, 한국어 코드포인트 이동·입력, 화면 크기 변경, CLI가 먼저 수정한 +문서의 충돌, 1 MiB 문서 끝의 1 B 추가와 새 CLI 프로세스의 복원, 키 명령과 +`SIGTERM` 종료 후 터미널 설정 복구를 통과했다. +[ASan·UBSan 검사](../../prototypes/connections/results/2026-09-24-fixed-tui-final/pty-sanitized.json)도 +같은 흐름을 통과했다. 검사 환경에서는 LeakSanitizer가 ptrace와 함께 실행되지 +않아 누수 탐지만 껐다. 코어의 코드 크기와 기본 CLI는 바뀌지 않았다. + +Linux AArch64/GCC 14 `-O2`의 [선택형 TUI 크기 게이트](../../prototypes/connections/results/2026-09-24-fixed-tui-final/profile.json)에서 +링크된 `text`는 **28,226 B**, 직접 공유 의존성은 libc뿐이었다. +[PTY 검사](../../prototypes/connections/results/2026-09-24-fixed-tui-final/pty-flow.json)의 +1 MiB 편집 피크 프로세스 RSS는 **3,168 KiB**로 4 MiB 회귀 상한 아래다. +이는 한 번의 실행 값이며 터미널 에뮬레이터, +커널 TTY 버퍼, 저장 파일·페이지 캐시를 포함하지 않는다. +[CLI+TUI 묶음](../../prototypes/connections/results/2026-09-24-fixed-tui-final/pure-document-cli-tui-linux-aarch64.tar.gz)은 +**33,654 B**이며 [다른 위치에서 해제·편집](../../prototypes/connections/results/2026-09-24-fixed-tui-final/package.json)을 +검증했다. 시스템 libc는 묶지 않았다. + +이 경로는 **실용적인 저메모리 TUI의 첫 검증**이다. 현재 TUI는 빌드 시 고정한 +문서 정의만 실행하고, 앱 정의 자체를 편집하지 않는다. 코드포인트 단위 +이동은 복합 문자소·터미널별 폭 처리와 동일하지 않다. 마우스, 접근성 트리, +IME 통합, GUI 레이아웃, 문서 목록·검색, macOS 빌드·실행은 아직 검증하지 +않았다. 따라서 GTK GUI와 기능 동등·메모리 동등 비교를 주장하지 않는다. + +재현 명령은 `make -C prototypes/connections check-tui`, 실행은 +`prototypes/connections/build/pure-edit-tui-fixed --store STORE note`다. diff --git a/docs/research/043-micro-package-boundary.md b/docs/research/043-micro-package-boundary.md new file mode 100644 index 0000000..2bba83e --- /dev/null +++ b/docs/research/043-micro-package-boundary.md @@ -0,0 +1,29 @@ +# 043 — 고정 실행기 전체 묶음의 크기 경계 + +문서 편집기는 작은 실행 코어와 선택형 호스트로 분리되어 있지만, 코어만 재면 +배포물이 커져도 마이크로 목표를 지켰다고 착각할 수 있다. 고정 정의 앱의 압축 +배포물 전체에 선택별 상한을 추가했다. `package_native.py --fixed`는 실행물 섹션, +CLI 작업 RSS, 선택형 TUI의 1 MiB 작업 RSS를 확인한 다음 아카이브를 만들고, +상한을 넘으면 파일을 공개하지 않는다. 상한은 +[`fixed-package-budget.json`](../../prototypes/connections/fixed-package-budget.json)에 있다. + +| 선택 | 상한 도입 당시 아카이브 | 회귀 상한 | 이동 후 확인한 작업 | +|---|---:|---:|---| +| CLI | 14,692 B | 16,384 B | 생성·읽기·버전 확인 편집 | +| CLI + HTTP | 32,548 B | 34,816 B | 같은 문서를 loopback HTTP로 편집 | +| CLI + TUI | 33,761 B | 35,840 B | PTY의 UTF-8·1 MiB 문서 저장 | +| CLI + HTTP + TUI | 51,424 B | 53,248 B | 두 호스트를 포함한 위 작업 | + +Linux AArch64, GCC 14 및 현재 libc에서 확인했다. 각 묶음에는 선택한 실행 파일, +매니페스트와 사용 문서가 들어가며 디버그 정보는 빠진다. 크기는 기능별로 중복 +링크된 코드를 **모두 포함한 압축 배포량**이다. 공유 libc, 터미널 에뮬레이터, +커널·파일 캐시, 저장 데이터는 포함하지 않는다. 실행 중 RSS는 별도의 1 MiB 문서 +작업으로 확인하며, TUI는 이전에 기록한 3,168 KiB 피크와 4 MiB 상한을 유지한다. +네 묶음과 각 이동 실행 결과는 +[`results/2026-09-24-micro-package-boundary`](../../prototypes/connections/results/2026-09-24-micro-package-boundary)에 보관했다. + +이 경계에 따라 다음 단계에서 고정 TUI에 `.pure` 파서와 소스 편집을 넣지 않는다. +정의 작성은 기존 소스 CLI와 선택형 창이 맡고, 고정 TUI는 준비된 앱의 사용 경로로 +남긴다. 작성과 실행을 한 바이너리로 합쳐야 할 실제 사용 사례가 나오면, 기능· +배포 크기·작업 RSS를 함께 측정한 뒤 경계를 다시 결정한다. 현재 수치로 일반 앱, +macOS 실행, 보안 피어, 저메모리 GUI의 비용을 주장할 수는 없다. diff --git a/docs/research/044-terminal-viewport-cost.md b/docs/research/044-terminal-viewport-cost.md new file mode 100644 index 0000000..2ee73e0 --- /dev/null +++ b/docs/research/044-terminal-viewport-cost.md @@ -0,0 +1,49 @@ +# 044 — 1 MiB 문서의 화면 갱신 비용과 여러 행 복원 + +고정 정의 터미널 편집기는 1 MiB 문서를 저장했지만, 키를 누를 때마다 커서 위치와 +화면 출력을 계산하면서 문서 처음부터 끝까지 다시 순회했다. 같은 편집기를 실제 +PTY 24×80에서 재생하니 끝에서 한 글자를 입력한 뒤 화면이 완성되기까지 한국어 +1 MiB 문서는 10회 중앙값 37.244 ms가 걸렸다. 여러 행·탭이 들어 있는 정상 +문서를 다시 열지 못하는 문제도 확인했다. 입력 검사가 `wcwidth()`의 줄바꿈·탭 +반환값을 일반 문자와 같이 처리한 것이 원인이었다. + +터미널 호스트 안에 **한 개의 화면 위치 앵커**를 두었다. 바이트 위치·화면 행·열· +터미널 폭을 기억하고, 그 앞을 수정하거나 폭을 바꾸면 무효화한다. 평소 입력과 +화살표 이동은 이 위치부터 보이는 범위만 해석한다. 멀리 이동하면 다시 순회해 +앵커를 만든다. 출력 범위 앞의 연속된 출력 가능 ASCII는 문자마다 `glyph()`을 +호출하지 않고 셀 수를 계산한다. 문서 크기에 비례하는 색인이나 별도 복사본은 +만들지 않았다. 줄바꿈·탭은 텍스트 검사에서 허용하고, 화면 계산은 기존 처리에 +맡긴다. 실행 코어와 `.pure` 계획은 바뀌지 않았다. + +같은 Linux AArch64 장치·CLI·PTY에서 [이전 보관 실행물](../../prototypes/connections/results/2026-09-24-fixed-tui-final/pure-document-cli-tui-linux-aarch64.tar.gz)과 +[현재 실행물](../../prototypes/connections/results/2026-09-24-tui-viewport/pure-document-cli-tui-linux-aarch64.tar.gz)을 +[`measure_tui_latency.py`](../../prototypes/connections/measure_tui_latency.py)로 비교했다. +각 문서를 열고 끝에 `Z`를 10회 순서대로 입력했다. 값은 **입력 전송부터 해당 +화면의 마지막 커서 표시까지**의 중앙값이며 문서 열기·저장·처리량은 포함하지 않는다. +두 실행물의 각 회차 PTY 화면 바이트 SHA-256은 모두 같았다. + +| 실제 문서 입력 | 이전 화면 갱신 중앙값 | 현재 | 비율 | +|---|---:|---:|---:| +| 256 B ASCII | 0.028 ms | 0.030 ms | 유사 | +| 1 MiB ASCII | 15.678 ms | 0.070 ms | 224× | +| 약 1 MiB 한국어 UTF-8 | 37.244 ms | 0.232 ms | 160× | + +[원자료 이전](../../prototypes/connections/results/2026-09-24-tui-viewport/latency-baseline.json), +[원자료 현재](../../prototypes/connections/results/2026-09-24-tui-viewport/latency-current.json). +이 수치는 이 장치·입력 위치·문서 구성에 한정된다. 처음 열기, 앵커보다 앞선 +편집, 화면 폭 변경, 먼 위치로 이동할 때는 전체 순회가 다시 필요할 수 있다. +완전한 문자소 단위 커서 이동·IME·다른 터미널 구현도 검증하지 않았다. + +[실제 PTY 검사](../../prototypes/connections/results/2026-09-24-tui-viewport/pty-flow.json)는 +여러 행·탭 문서의 CLI 생성→TUI 편집→재열기, 60행 위쪽 이동, 1 MiB 한국어 +문서의 앵커 앞 편집과 저장·복원을 확인한다. [ASan·UBSan 검사](../../prototypes/connections/results/2026-09-24-tui-viewport/pty-sanitized.json)도 +같은 흐름을 통과했다. LeakSanitizer는 실행 환경의 ptrace 제약으로 껐다. + +선택형 TUI `text`는 [28,226 B에서 28,762 B](../../prototypes/connections/results/2026-09-24-tui-viewport/profile.json)로 +536 B 증가했고 libc 외 직접 공유 의존성은 없다. 실제 1 MiB ASCII 편집의 +이번 프로세스 피크 RSS는 3,168 KiB였으며 4 MiB 회귀 상한을 통과했다. +이 RSS를 앞선 단일 실행 값보다 줄었다는 근거로 사용하지 않는다. CLI+TUI +[이동 묶음](../../prototypes/connections/results/2026-09-24-tui-viewport/package.json)은 +34,216 B로 선택 구성의 35,840 B 상한 안이다. HTTP까지 선택한 +[전체 묶음](../../prototypes/connections/results/2026-09-24-tui-viewport/package-http-tui.json)은 +51,844 B로 해당 53,248 B 상한을 통과했다. diff --git a/docs/research/045-owner-thread-async-effects.md b/docs/research/045-owner-thread-async-effects.md new file mode 100644 index 0000000..f866f5f --- /dev/null +++ b/docs/research/045-owner-thread-async-effects.md @@ -0,0 +1,51 @@ +# 045 — 코어가 효과의 완료를 기다리지 않는 선택형 경로 + +현재 문서 서비스의 `pure_runtime_step()`은 저장소 호스트 콜백이 반환할 때까지 +실행 영역을 붙잡는다. 큰 저장 작업이나 원격 효과가 생기면 요청 슬롯의 수명· +본문 예산을 유지하면서 다른 작업을 진행할 길이 필요하다. 이 단계에서는 +**선택형 비동기 효과 계약**을 실행 코어에 붙였다. 기본 CLI·HTTP·TUI는 기존 +동기 경로를 그대로 링크한다. + +`PURE_ASYNC_EFFECTS` 빌드에서 `pure_runtime_take()`는 준비된 READ·WRITE·SPLICE +효과와 세대가 있는 티켓을 호스트에 넘긴다. 효과에는 저장 키, 조건부 버전, +선택된 본문, 읽기 범위와 가용 예산이 포함된다. 호스트가 파일·네트워크 작업을 +마친 후 **소유 스레드에서** `pure_runtime_complete()`를 호출한다. 완료 순서는 +자유지만 저장 효과의 버전 조건은 여전히 해당 호스트에서 원자적으로 검사한다. +준비 중 범위가 유효하지 않은 입력은 호스트에 전달하기 전에 완료된다. + +호출자가 진행 중 티켓을 놓아도 입력은 작업이 완료될 때까지 남는다. 읽기 본문을 +다른 영역으로 전달하면 소비자가 완료·해제될 때까지 원본 매핑을 유지한다. +동시에 여러 READ가 완료되면 **완료 시점의 남은 예산**으로 다시 검사한다. +예산을 넘은 결과는 소비자에게 공개하지 않고 호스트 자원을 회수한다. 호스트가 +잘못된 소유 자원을 넘긴 완료 호출은 거절하고 자원과 작업을 그대로 둔다. + +[실제 저장소 검사](../../prototypes/connections/test-async.c)는 대기 중인 파일 쓰기의 +티켓을 먼저 놓고, 다른 문서를 먼저 저장한 뒤 첫 작업을 완료한다. 종료 요청 +중에는 재시작을 막고, 마지막 완료 후에만 허용한다. 저장된 값의 READ→다른 +영역 WRITE도 같은 매핑을 복사 없이 전달한다. 추가로 기존 +[`editor.pure`](../../prototypes/connections/examples/editor.pure)의 SPLICE 생성·추가· +낡은 버전 충돌·GET을 이 경로로 실행한다. 이 검사는 실제 파일과 한 작업자 +스레드를 사용하지만 HTTP 처리량을 측정하는 실험은 아니다. + +| 선택한 범위 | `text` | 영역 기록 | 작업 슬롯 | 효과 값 | +|---|---:|---:|---:|---:| +| 기본 실행 코어 | 5,932 B | 200 B | 224 B | 없음 | +| 비동기 완료 선택 코어 | 7,312 B | 200 B | 224 B | 호출자 저장 공간 128 B | + +[기본 코어 원자료](../../prototypes/connections/results/2026-09-24-async-effects/default-core.json), +[선택 코어 원자료](../../prototypes/connections/results/2026-09-24-async-effects/async-core.json). +두 아카이브 모두 파서·저장소·소켓·스레드 라이브러리를 포함하지 않는다. +선택 코어 증가량은 1,380 B이며 인스턴스·슬롯 반복 메모리는 증가하지 않았다. +실제 작업자 스택, 저장소, 호스트의 완료 큐는 이 수치에 **포함되지 않는다**. +[`make check-async`](../../prototypes/connections/Makefile)와 +[ASan·UBSan 실행](../../prototypes/connections/results/2026-09-24-async-effects/sanitized.json), +기존 `make check-release`가 통과했다. 검사 환경의 ptrace 제약 때문에 +LeakSanitizer는 껐다. + +이것은 **비동기 호스트를 위한 수명·완료 원시 기능**이다. 기본 HTTP·TLS 피어는 +동기 효과를 호출하며, 선택형 HTTP 호스트의 실제 연결은 [046](046-optional-async-document-http.md)에 기록했다. 시작한 효과의 취소도 호스트가 I/O 중단을 확인한 뒤 +완료로 돌려줘야 하며, 코어가 강제로 외부 효과를 되감지 않는다. 같은 저장 키의 +순서가 필요한 호스트는 키별 큐나 조건부 버전으로 이를 보장해야 한다. 실제 +HTTP 호스트의 완료 신호·연결 종료·기한·프로세스 종료와 **전체 앱** 메모리는 +[046](046-optional-async-document-http.md)에서 확인했다. 인증 피어와 동등 서버의 +지연 비교는 남아 있다. diff --git a/docs/research/046-optional-async-document-http.md b/docs/research/046-optional-async-document-http.md new file mode 100644 index 0000000..dd3c0a5 --- /dev/null +++ b/docs/research/046-optional-async-document-http.md @@ -0,0 +1,55 @@ +# 046 — 실제 HTTP 호스트에서 선택형 비동기 효과 실행 + +기존 HTTP 문서 호스트는 파일 저장 효과를 이벤트 루프 안에서 실행했다. 저장소의 +`fsync`가 지연되면 이미 연결된 다른 클라이언트의 헤더·기한·종료도 처리하지 +못한다. [045](045-owner-thread-async-effects.md)의 선택형 완료 계약을 **기존 +`editor.pure`와 기존 HTTP 호스트**에 연결했다. 기본 CLI·HTTP는 그대로 단일 +스레드와 동기 코어를 사용한다. + +`make async-server`는 파일 효과를 하나의 작업자에 넘긴다. 이벤트 루프는 +요청 입력·응답 출력·기한·시그널과 `pure_runtime_complete()`를 소유한다. +저장소 코드는 현재 프로세스 안에서 단일 스레드 사용을 전제로 하고 +임시 파일 번호를 공유하므로 작업자를 여러 개 띄우지 않았다. 최대 8개 작업 +슬롯과 작업자 스택 128 KiB가 이 선택 호스트의 정적 상한이다. 완료 신호에는 +이미 사용하던 깨움 파이프를 재사용한다. 연결이 기한 만료나 종료로 사라져도 +작업자가 빌린 입력은 완료할 때까지 보존하고, 완료를 소유 스레드에서 회수한다. + +[결정적 중단 검사](../../prototypes/connections/results/2026-09-24-async-http/gated.json)는 +테스트 전용 저장소 훅으로 파일 동기화 직전 작업자를 멈춘다. 그 동안 두 번째 +HTTP 연결의 잘못된 요청은 400으로 응답하고, 첫 연결은 기한에 닫힌다. 작업자를 +풀면 저장 완료와 본문 회수가 확인되며, 별도 시나리오에서 종료 신호가 진행 중인 +작업의 완료를 기다린다. 작업자가 멈춰 있는 동안 두 번째 **저장 효과**까지 +진행된다는 뜻은 아니다. 한 작업자가 효과를 순서대로 처리한다. 테스트 훅은 +배포 실행물에 링크되지 않는다. 같은 검사의 [ASan·UBSan 실행](../../prototypes/connections/results/2026-09-24-async-http/sanitized.json)도 통과했다. + +기존 [HTTP 정확성 검사](../../prototypes/connections/results/2026-09-24-async-http/full-check.json)를 +이 선택 호스트에 다시 적용했다. 이어서 +[서비스 작업 원자료](../../prototypes/connections/results/2026-09-24-async-http/service-scale-memory.json)에서 +고정 정의·소스 정의·선택형 비동기 소스 정의 호스트를 각각 실제 문서 +1,024개, 요청 4,096개, 동시 클라이언트 8개로 실행했다. 세 실행의 최종 문서 +해시와 저장 버전이 일치하고, 모두 종료 시 보유 payload 0 B·기한 만료 0건이다. + +| Linux AArch64 / GCC 14, 이 작업 | 기존 소스 HTTP | 선택형 비동기 소스 HTTP | +|---|---:|---:| +| 실행물 `text` | 43,778 B | 47,897 B | +| 실행물 `data` | 1,068 B | 1,180 B | +| 서버 구조체 | 41,528 B | 43,224 B | +| 유휴 프로세스 RSS | 1,536 KiB | 1,536 KiB | +| 이 작업의 피크 RSS | 1,712 KiB | 1,712 KiB | +| 유휴 가상주소 공간 | 2,464 KiB | 2,656 KiB | +| 이 작업의 피크 가상주소 공간 | 2,528 KiB | 2,736 KiB | +| 프로세스 스레드 | 1 | 2 | + +RSS의 동일한 숫자는 이 표본에서의 관측값이며 비동기 호스트의 일반적인 +메모리 우위를 뜻하지 않는다. 작업자 스택과 저장소 파일·페이지 캐시·커널 +소켓 버퍼는 실행물의 코드 섹션에 포함되지 않는다. [크기 상한 검사](../../prototypes/connections/results/2026-09-24-async-http/profile.json)는 +선택 호스트 `text` 48 KiB 이내와 직접 동적 의존성을 확인한다. 기본 HTTP의 +`text`는 여전히 43,778 B이고 `make check-release`가 통과했다. + +이 단계는 저장소의 지연이 **이벤트 루프 전체를 멈추지 않는 것**과 그 비용을 +입증한다. 단일 작업자가 모든 저장 효과를 직렬화하므로 효과 처리량이나 +RPC 지연이 빨라졌다는 주장은 하지 않는다. 기한이 지난 뒤 이미 접수된 쓰기는 +취소되지 않고 커밋될 수 있다. 이는 결과를 받지 못한 클라이언트가 같은 +조건부 버전으로 무조건 재시도하면 안 된다는 뜻이다. 이후 과제는 작업자 +단일성에 묶인 저장소 임시 이름과 직렬화를 재설계하고, 실제 인증 피어까지 +같은 완료·기한 모델을 적용한 뒤 동등한 서버와 지연 분포를 비교하는 것이다. diff --git a/docs/research/047-bounded-native-document-browser.md b/docs/research/047-bounded-native-document-browser.md new file mode 100644 index 0000000..ee6b45e --- /dev/null +++ b/docs/research/047-bounded-native-document-browser.md @@ -0,0 +1,46 @@ +# 047 — 저메모리 네이티브 편집기에서 문서 찾기 + +고정 정의 TUI는 키를 알고 있으면 다른 문서를 열 수 있었지만, 저장된 키를 +찾는 방법이 없었다. 그래서 실제 앱에서 이미 만든 문서를 다시 선택하려면 +외부 파일 목록을 봐야 했다. 편집기의 **Ctrl+L 문서 목록**을 저장소 호스트에 +추가했다. 화살표·Home·End로 이동하고 Enter로 연다. Ctrl+O는 새 키 입력을 +계속 담당한다. 미저장 초안이 있으면 목록으로 전환하지 않는다. + +목록은 저장소 디렉터리의 `obj-KEY` 이름을 한 번 읽어 정렬한 스냅샷이다. +한 키에 65 B를 사용하고 8,192개(최대 532,480 B)에서 멈춘다. 한도를 넘거나 +디렉터리 읽기에 실패하면 불완전한 목록을 보여주지 않고 기존 문서를 유지한다. +목록을 닫으면 배열을 해제한다. 삭제된 키를 선택해도 새 빈 문서로 조용히 +전환하지 않고 오류를 보여준다. 실제 본문을 열 때는 기존 런타임의 검증된 +READ 경로를 통과한다. 코어·저장 형식·작성 언어·고정 실행 계획은 바뀌지 않았다. + +[실제 PTY 검사](../../prototypes/connections/results/2026-09-24-terminal-document-list/tui.json)는 +문서 전환, 초안 보호, 목록 이후 삭제된 키, 1 MiB 편집을 확인한다. 별도 +저장소에 **1,024개 실제 문서**를 CLI로 만들고 TUI의 정렬된 목록에서 마지막 +키를 열었다. 같은 경로의 [ASan·UBSan 실행](../../prototypes/connections/results/2026-09-24-terminal-document-list/sanitized.json)도 +통과했다. LeakSanitizer는 검사 환경의 ptrace 제약으로 껐다. + +| Linux AArch64 / GCC 14 | 앞선 TUI | 문서 목록 포함 TUI | +|---|---:|---:| +| 링크된 `text` | 28,762 B | 31,303 B | +| 링크된 `data` | 988 B | 1,036 B | +| 1 MiB 문서 편집 피크 RSS | 3,168 KiB | 2,832 KiB | +| 1,024키 목록 피크 RSS | 미측정 | 1,856 KiB | +| CLI+TUI 압축 묶음 | 34,216 B | 35,642 B | +| CLI+HTTP+TUI 압축 묶음 | 51,844 B | 53,385 B | + +[TUI 코드 섹션 상한](../../prototypes/connections/results/2026-09-24-terminal-document-list/profile.json)은 +기능을 확인한 뒤 29,696→32,768 B로 검토해 높였다. 결합 묶음의 상한도 +53,248→54,272 B로 높였고 CLI 전용 16,384 B 상한은 유지했다. 이번 +[CLI 전용 묶음](../../prototypes/connections/results/2026-09-24-terminal-document-list/cli-check.json)은 +14,706 B다. 새 [CLI+TUI](../../prototypes/connections/results/2026-09-24-terminal-document-list/cli-tui-check.json)와 +[CLI+HTTP+TUI](../../prototypes/connections/results/2026-09-24-terminal-document-list/cli-http-tui-check.json)는 +모두 다른 위치에 풀어 실제 명령·PTY·HTTP를 실행했다. 이 압축 크기는 libc나 +실행 메모리가 아니다. RSS 값은 서로 다른 한 번의 실행에서 관찰한 값이며 +목록 추가로 1 MiB 편집 메모리가 줄었다는 증거가 아니다. 두 경우 모두 기존 +4 MiB 회귀 상한 안에 있다. + +목록은 편집 중 스토어 변화를 자동 추적하지 않는다. Ctrl+L로 다시 읽을 수 있고, +키를 여는 순간 본문과 버전을 다시 검증한다. 8,192개를 넘는 대형 저장소에는 +페이지 탐색이나 검색 색인이 필요하다. 이 선택 UI의 목록은 그런 범용 색인으로 +주장하지 않는다. `.pure` 정의 작성은 기존 별도 작성 호스트의 책임이므로 +고정 실행 TUI에 파서와 컴파일러를 링크하지 않았다. diff --git a/docs/research/048-immutable-read-write-overlap.md b/docs/research/048-immutable-read-write-overlap.md new file mode 100644 index 0000000..469ef0d --- /dev/null +++ b/docs/research/048-immutable-read-write-overlap.md @@ -0,0 +1,56 @@ +# 048 — 저장 쓰기 중에도 기존 문서를 읽는 선택형 호스트 + +[046](046-optional-async-document-http.md)의 선택형 HTTP 호스트는 파일 효과를 +작업자 하나에 넘겨 이벤트 루프를 보호했다. 다만 쓰기가 `fsync`에서 멈추면 다른 +문서 읽기도 그 작업자 뒤에서 대기했다. 저장소 코드를 확인하면 읽기는 파일 버전을 +열어 불변 `mmap`으로 유지하고, 쓰기만 임시 파일 번호와 `.writer.lock`을 사용한다. +따라서 **읽기 작업자 하나와 쓰기 작업자 하나**를 두되, 기존의 쓰기 직렬화는 +유지하는 경계가 가능하다. + +선택형 `PURE_HTTP_ASYNC` 호스트에서 두 작업자는 입력 종류에 따라 공유된 +최대 8개 작업 슬롯을 탐색한다. 각 작업자의 스택 크기는 128 KiB로 설정했다. +HTTP 연결과 `pure_runtime_complete()`는 기존처럼 소유 스레드에서 처리한다. +기본 코어·CLI·HTTP에는 이 작업자들이 링크되지 않는다. 새 앱 모델이나 공용 +코어 연산은 추가하지 않았다. + +[결정적 HTTP 검사](../../prototypes/connections/results/2026-09-24-async-read-write/gated.json)는 +실제 CLI로 `gated=old` 버전 1을 만든 뒤, HTTP 수정 `oldafter`를 파일 동기화 +직전에 멈춘다. 쓰기를 풀기 **전에** 같은 문서의 GET이 `old`, ETag `"1"`을 +반환한다. 이때 잘못된 다른 HTTP 요청도 400으로 답하고, 쓰기를 기다린 연결은 +기한에 닫힌다. 쓰기를 풀면 GET이 `oldafter`, ETag `"2"`를 반환한다. 종료 중인 +별도 실행에서도 진행 중인 쓰기 완료를 기다리고 종료 시 보유 본문은 0 B다. +이 검사는 한 작업자로 모든 파일 효과를 실행하던 호스트에서는 통과하지 않는다. +[ASan·UBSan 실행](../../prototypes/connections/results/2026-09-24-async-read-write/sanitized.json)과 +[기존 HTTP 정확성 전체 검사](../../prototypes/connections/results/2026-09-24-async-read-write/full-check.json)도 +통과했다. + +[서비스 작업 원자료](../../prototypes/connections/results/2026-09-24-async-read-write/service-scale.json)는 +고정 정의·소스 정의·선택형 비동기 소스 정의로 각각 8 KiB 문서 1,024개를 +만들고 8개 클라이언트가 4,096개 요청을 처리한 결과다. 세 호스트의 최종 문서 +해시와 저장 파일 수가 같고, 모두 보유 본문 0 B로 종료했다. 이 실행의 비용은 +다음과 같다. + +| Linux AArch64 / GCC 14 | 기본 소스 HTTP | 선택형 읽기+쓰기 HTTP | +|---|---:|---:| +| 링크된 `text` | 43,778 B | 48,401 B | +| 서버 구조체 | 41,528 B | 43,320 B | +| 유휴 RSS | 1,520 KiB | 1,552 KiB | +| 피크 RSS | 1,696 KiB | 1,728 KiB | +| 유휴 가상주소 공간 | 2,464 KiB | 2,848 KiB | +| 피크 가상주소 공간 | 2,528 KiB | 2,928 KiB | +| 프로세스 스레드 | 1 | 3 | + +선택형 호스트는 기본 호스트보다 코드 4,623 B, 구조체 1,792 B, 이 작업의 +피크 RSS 32 KiB가 더 든다. [이전 단일 작업자 기록](046-optional-async-document-http.md)과 +비교하면 `text` 504 B와 구조체 96 B가 늘었다. 두 128 KiB 스택은 코드 섹션 +크기에 포함되지 않는다. [크기 상한](../../prototypes/connections/results/2026-09-24-async-read-write/profile.json)은 +기존 선택형 48 KiB 상한을 올리지 않고 통과했다. `make check-release`에서도 +기본 코어 `text` 5,932 B, 기본 소스 HTTP `text` 43,778 B를 유지했다. + +이는 **느린 쓰기와 기존 버전 읽기의 독립성 및 스냅샷 정확성**에 대한 결과다. +읽기끼리, 쓰기끼리는 각각 직렬이며, 기한이 지난 쓰기도 취소되지 않고 커밋될 +수 있다. 매핑되지 않은 파일 캐시와 커널 소켓 버퍼는 프로세스 RSS 표에 포함되지 +않는다. 이 작업은 처리량이나 +Kitex급 RPC 성능을 측정하지 않는다. 일반 네트워크·UI 효과까지 같은 완료 모델에 +연결하려면 저장소형 `READ/WRITE/SPLICE` 계약에서 벗어나는 별도 설계와 앱 검증이 +필요하다. diff --git a/docs/research/049-idle-app-work-slots.md b/docs/research/049-idle-app-work-slots.md new file mode 100644 index 0000000..27c22d7 --- /dev/null +++ b/docs/research/049-idle-app-work-slots.md @@ -0,0 +1,101 @@ +# 049 — 앱이 쉴 때 작업 슬롯도 비운다 + +Pure 실행 영역은 호출자가 제공한 작업 슬롯을 보유한다. 기존 네이티브 작성 앱은 +입력 한 건에는 슬롯 하나, 저장·복원의 `READ → WRITE` 전달에는 둘이 필요해서 +**모든 유휴 인스턴스에 `PureWork[2]` 448 B를 계속 예약**했다. 이전에 계산 정의를 +공유해 scalar 인스턴스를 280 B까지 줄인 [028](028-shared-fixed-program.md) 이후에는 +이 448 B가 독립 앱의 반복 비용에서 더 큰 부분이었다. + +코어에 `pure_runtime_bind()`를 추가했다. 영역은 작업 슬롯 없이 시작할 수 있고, +호스트가 진행 중인 호출에 필요한 슬롯을 제공한다. 네이티브 앱은 입력에 1개, +저장·복원에 2개를 **소유 스레드의 호출 스택에만** 두고, 결과·대여가 모두 회수된 +뒤 분리한다. 슬롯을 바꿔도 영역의 예산·누적 계수·티켓 세대는 유지된다. 진행 중 +작업, 완료를 보유한 티켓, 다른 영역이 고정한 본문이 있으면 분리를 거절한다. +[코어의 실제 대여·세대 검사](../../prototypes/connections/test-runtime.c)는 수신 영역이 +원본 본문을 소비할 때까지 분리가 실패하고, 회수 뒤 성공하며, 오래된 티켓이 새 +슬롯을 가리키지 못함을 확인한다. 선택형 장시간 비동기 HTTP 호스트는 계속 고정 +슬롯을 사용한다. 이 변경은 동기 앱의 호출 수명에만 적용한다. + +[전체 앱 메모리 A/B 원자료](../../prototypes/workbench/results/2026-09-24-idle-work-binding/app-memory.json)는 +같은 준비된 `routed-layout.pure`와 같은 scalar 아카이브로 각각 **독립 앱 +1,000개**를 초기화한다. 각 앱이 코어를 통해 입력을 처리하고 서로 다른 문서를 +저장하며, 다섯 앱을 다시 복원한다. 실행마다 접수와 회수가 3,010건씩 일치하고 +저장 파일을 확인한다. 이전 앱 소스는 [동결된 작성 호스트 +소스](../../prototypes/workbench/results/2026-09-24-native-desktop/sources/native/app.c)와 +[헤더](../../prototypes/workbench/results/2026-09-24-native-desktop/sources/native/app.h)를 +그대로 사용했다. 비교 하네스는 현재 소스에서 두 실행물을 다시 빌드한다. +Linux에서 Zig 0.15.2가 있다면 저장소 루트에서 +`python3 prototypes/workbench/manage.py native prototypes/workbench/examples/routed-layout.pure` +후 `(cd prototypes/workbench && python3 native/check_app_memory.py)`로 재현한다. + +| Linux AArch64 / GCC 14, 같은 실제 앱 1,000개 | 이전 고정 슬롯 | 호출 중 대여 | +|---|---:|---:| +| 앱 컨트롤러 / 인스턴스 | 928 B | 480 B | +| scalar 상태 / 인스턴스 | 280 B | 280 B | +| 초기화 후 RSS 증가, 3회 중앙값 | 1,264 KiB | 832 KiB | +| 저장 후 RSS 증가, 3회 중앙값 | 1,328 KiB | 896 KiB | +| A/B 하네스 실행물 `text` | 35,698 B | 35,962 B | + +컨트롤러에서는 인스턴스당 448 B, 실제 프로세스 RSS 증가량에서는 이 작업의 +**432 KiB**가 줄었다. A/B 둘 다 변경 후의 같은 코어를 링크했고, 앱 호스트만 +다른 실행물의 `text` 차이는 264 B다. 공통 코어의 +[최신 크기 검사](../../prototypes/connections/results/2026-09-24-idle-work-binding/core.json)는 +`text` 5,932→6,076 B, 영역 200 B, 작업 슬롯 224 B다. 코어의 기존 +6,144 B 상한은 올리지 않았다. 코드 증분을 숨기지 않으면서 유휴 앱 1,000개의 +반복 예약을 줄인 결과다. + +메모리가 사라진 것은 아니다. [GCC 정적 스택 추정](../../prototypes/workbench/results/2026-09-24-idle-work-binding/stack-baseline.su)을 +[새 실행](../../prototypes/workbench/results/2026-09-24-idle-work-binding/stack-elastic.su)과 +비교하면 `pure_native_app_action`은 208→432 B, 저장·복원 공통 `transfer`는 +288→736 B다. 각각 224 B와 448 B를 **호출 중에** 스택으로 옮겼다. 이 수치는 +함수 자신의 정적 스택 추정이며 호출 체인 전체의 피크가 아니다. 한 소유 스레드가 +동기 호출을 진행하는 현재 앱 계약에 맞는 선택이고, 작업을 반환 뒤에도 보유하는 +호스트에는 이 배치를 적용하지 않는다. + +## 선택한 배포 묶음의 비용 + +변경 후 기존 `--strip-debug` 방식의 CLI+TUI 압축 묶음은 +[35,847 B](../../prototypes/connections/results/2026-09-24-idle-work-binding/precompact-budget-failure.txt)가 되어 +35,840 B 상한을 7 B 넘었다. 상한을 올리는 대신 고정 실행 묶음에서는 실행에 +필요 없는 ELF 심볼도 제거하도록 했다. 디버깅용 빌드와 소스 작성 묶음은 그대로 +두며, `text` 코드 섹션이나 프로세스 RSS가 작아졌다고 주장하지 않는다. +패키지 매니페스트에는 실행마다 달라지는 RSS 표본을 넣지 않고, 크기 상한과 +실행물 식별자만 남겼다. 실제 RSS 표본은 별도 검사 JSON에 보관한다. +동일한 의존성 검사를 거쳐 새 위치에서 실제 실행한 결과는 다음과 같다. + +| 고정 정의 선택 | 압축 묶음 | 회귀 상한 | 이동 후 동작 | +|---|---:|---:|---| +| [CLI](../../prototypes/connections/results/2026-09-24-idle-work-binding/cli.tar.gz) | 12,987 B | 16,384 B | [검증](../../prototypes/connections/results/2026-09-24-idle-work-binding/cli-check.json) | +| [CLI+HTTP](../../prototypes/connections/results/2026-09-24-idle-work-binding/cli-http.tar.gz) | 28,719 B | 34,816 B | [검증](../../prototypes/connections/results/2026-09-24-idle-work-binding/cli-http-check.json) | +| [CLI+TUI](../../prototypes/connections/results/2026-09-24-idle-work-binding/cli-tui.tar.gz) | 31,726 B | 35,840 B | [검증](../../prototypes/connections/results/2026-09-24-idle-work-binding/cli-tui-check.json) | +| [CLI+HTTP+TUI](../../prototypes/connections/results/2026-09-24-idle-work-binding/cli-http-tui.tar.gz) | 47,419 B | 54,272 B | [검증](../../prototypes/connections/results/2026-09-24-idle-work-binding/cli-http-tui-check.json) | + +소스 `.pure`를 묶어 실행 시 정의를 읽는 [작성 CLI 묶음](../../prototypes/connections/results/2026-09-24-idle-work-binding/source-cli.tar.gz)은 +22,595 B이고 [이동 후 편집·복원](../../prototypes/connections/results/2026-09-24-idle-work-binding/source-cli-check.json)을 +확인했다. 고정 정의 묶음과 기능·크기를 동등한 것으로 취급하지 않는다. +앞선 [047의 CLI+TUI 묶음](047-bounded-native-document-browser.md)은 35,642 B였다. +현재 묶음은 코어 기능이 늘어난 뒤에도 3,916 B 작다. 이 차이는 패키지에서 +사용하지 않는 심볼을 제거한 효과다. 작성 CLI와 고정 정의 네 선택을 각각 다시 빌드한 +[아카이브 바이트 해시](../../prototypes/connections/results/2026-09-24-idle-work-binding/package-reproducibility.json)도 +일치했다. 프로그램 실행 메모리 절감은 위 1,000개 앱 A/B에서 별도로 확인했다. + +[기본 릴리스 검사](../../prototypes/connections/results/2026-09-24-idle-work-binding/release.log), +[선택형 비동기 코어](../../prototypes/connections/results/2026-09-24-idle-work-binding/async-core.json), +[실제 HTTP 쓰기 중 읽기](../../prototypes/connections/results/2026-09-24-idle-work-binding/async-gated.json), +[1,024개 문서 서비스](../../prototypes/connections/results/2026-09-24-idle-work-binding/service-scale.json), +[PTY 문서 편집기](../../prototypes/connections/results/2026-09-24-idle-work-binding/tui.json), +[인증 문서 피어](../../prototypes/connections/results/2026-09-24-idle-work-binding/document-peer.json)가 +통과했다. 작성 앱의 [독립 상태·충돌·정의 교체 +검사](../../prototypes/workbench/results/2026-09-24-idle-work-binding/native-unit.json), +[C 코드 ASan·UBSan](../../prototypes/workbench/results/2026-09-24-idle-work-binding/native-sanitized.json), +[인증 scalar 피어](../../prototypes/workbench/results/2026-09-24-idle-work-binding/scalar-peer.json)도 +통과했다. 공통 코어의 [ASan·UBSan 검사](../../prototypes/connections/results/2026-09-24-idle-work-binding/runtime-sanitized.json)는 +대여 중인 슬롯과 오래된 티켓을 포함한다. + +이 A/B는 같은 프로세스의 1,000개 **동기 네이티브 앱**에 대한 메모리 결과다. +실제 네트워크 서비스 1,000명 처리량, 불신 코드 격리, macOS RSS를 증명하지 않는다. +Zig 0.15.2 실행 파일이 현재 환경에 없어서 scalar 아카이브를 다시 빌드하지는 +못했다. 준비된 정의를 현재 소스로 재생성해 기존 아카이브 프로필과 일치함을 +검사했고 A/B 모두 동일한 아카이브를 링크했다. 코어·C 호스트는 현재 소스에서 +재빌드했다. 다음 연구에서는 유휴 영역 자체의 200 B와 일반 I/O 계약을 함께 +검토해야 하며, 슬롯 절감을 이유로 수명·예산의 귀속을 약화해서는 안 된다. diff --git a/docs/research/050-bounded-document-prefix-find.md b/docs/research/050-bounded-document-prefix-find.md new file mode 100644 index 0000000..a732be3 --- /dev/null +++ b/docs/research/050-bounded-document-prefix-find.md @@ -0,0 +1,33 @@ +# 050 — 문서 1,024개에서 추가 인덱스 없이 이름 찾기 + +선택형 네이티브 TUI는 저장소에서 문서 이름을 읽어 정렬하고 8,192개까지만 보유한다. +기존에는 화살표와 처음·끝 이동만 있어, 1,024개 실제 문서에서 중간 이름을 찾는 데 +목록을 길게 이동해야 했다. 목록을 연 상태에서 이름 접두어를 입력하면 정렬된 기존 +배열을 이진 탐색해 첫 일치 문서로 이동한다. Backspace는 접두어를 줄이고, +일치하지 않는 이름에는 Enter를 눌러도 문서를 열지 않는다. 화살표를 누르면 +접두어를 지우고 기존 목록 탐색으로 돌아간다. 검색용 배열·인덱스·상주 버퍼는 +만들지 않고 기존 65 B 키 입력 공간을 재사용했다. + +[실제 PTY 검사](../../prototypes/connections/results/2026-09-24-prefix-find/tui.json)는 +CLI로 문서 1,024개를 만든 뒤 `doc0919`를 찾아 열고, 없는 `zz`를 열지 않으며, +접두어를 지운 후 `doc1023`을 여는 작업을 검증한다. 저장·충돌·UTF-8·1 MiB 편집과 +터미널 복구 검사도 통과했다. [선택형 TUI 릴리스 프로필](../../prototypes/connections/results/2026-09-24-prefix-find/tui-profile.json)은 +Linux AArch64 / GCC 14에서 링크된 `text` **31,463→32,175 B(+712 B)**, +data 1,036 B, BSS 24 B로 기존 32,768 B 상한 안에 있다. 실행 코어에는 +이 검색 코드가 없으며 직전 [코어 검사](../../prototypes/connections/results/2026-09-24-idle-work-binding/core.json)의 +6,076 B 상한도 그대로다. + +[CLI+TUI 묶음](../../prototypes/connections/results/2026-09-24-prefix-find/cli-tui.tar.gz)은 +31,726→**32,233 B(+507 B)**로 기존 35,840 B 상한 안에 있다. [다른 위치에서 +풀어 실행한 검사](../../prototypes/connections/results/2026-09-24-prefix-find/package-check.json)는 +고정 정의 CLI의 조건부 편집과 선택형 TUI의 실제 작업을 확인했다. +이번 1 MiB 문서 PTY 실행의 피크 RSS는 3,200 KiB, 1,024개 목록에서는 +1,904 KiB였다. 이전 1 MiB 표본 2,832 KiB보다 높으므로 RAM이 줄었다고 +주장하지 않는다. 모두 기존 4,096 KiB 회귀 상한 안의 단일 호스트 표본이다. + +macOS의 엄격한 X/Open 네임스페이스는 TUI가 사용하는 `O_NOFOLLOW`를 숨길 수 있다. +[Apple의 fcntl.h](https://github.com/apple-oss-distributions/xnu/blob/main/bsd/sys/fcntl.h)를 +근거로 해당 TUI 소스에서만 Darwin 확장을 노출하고, `check-portable`에서 TUI +실행물까지 컴파일하도록 했다. Linux AArch64에서는 C 검사·실제 PTY가 통과했다. +Apple Silicon SDK 컴파일·실행은 아직 확인하지 않았으며, 이름 접두어 이동은 +문서 본문 검색이 아니다. diff --git a/docs/research/051-optional-runtime-metrics.md b/docs/research/051-optional-runtime-metrics.md new file mode 100644 index 0000000..6e8ae1c --- /dev/null +++ b/docs/research/051-optional-runtime-metrics.md @@ -0,0 +1,59 @@ +# 051 — 실행 영역의 계측을 선택형으로 분리 + +마이크로 런타임의 영역마다 최고 사용량과 누적 작업 수를 항상 저장할 필요는 없다. +`retained`, `active`, 예산, 티켓 세대는 안전한 실행에 남기고, 최고치·누적 +접수·읽기·쓰기·충돌·회수·대여 출력 바이트는 `PURE_RUNTIME_METRICS` 빌드에서만 +보관한다. 기본 CLI·TUI·네이티브 앱·TLS 피어는 작은 구조체를 사용한다. 통계 +응답을 제공하는 HTTP 호스트와 계수 검증 테스트는 계측 빌드로 유지했다. +서로 다른 구조체 배치의 오브젝트가 섞이면 초기화 심볼 +`pure_runtime_init`/`pure_runtime_init_metrics`의 차이로 링크가 실패한다. +이 구조체는 외부 ABI가 아니며, 한 실행물의 모든 컴파일 단위에 같은 설정을 써야 한다. + +| Linux AArch64, GCC 14 `-O2` | 기본 | 계측 선택 | +|---|---:|---:| +| `PureRuntime` / 영역 | 128 B | 200 B | +| `runtime.o + evaluate.o`의 `text` | [5,908 B](../../prototypes/connections/results/2026-09-24-compact-runtime/core.json) | 6,076 B | +| 코어 `data` / `bss` | 0 / 0 B | 0 / 0 B | +| 유휴 네이티브 앱 컨트롤러 | 408 B | 480 B | + +[실제 앱 1,000개 세 방식 비교](../../prototypes/workbench/results/2026-09-24-compact-runtime/app-memory-three-way-final.json)는 +동일한 준비된 `routed-layout.pure`·scalar 아카이브·현재 C 코어로 실행했다. +각 앱이 입력 한 건을 처리하고 서로 다른 문서를 저장하며, 다섯 앱은 복원도 했다. +세 방식 모두 입력 1,000건과 저장·복원 1,005건이 성공했고, 종료 시 활성 작업과 +보관 바이트는 0이었다. 세 번 실행한 RSS 증가량의 중앙값은 다음과 같다. + +| 1,000개 실제 앱 | 컨트롤러 / 앱 | 초기화 후 RSS 증가 | 저장 후 RSS 증가 | +|---|---:|---:|---:| +| 이전 상시 작업 슬롯, 작은 코어 | 856 B | 1,200 KiB | 1,264 KiB | +| 호출 중 슬롯, 작은 코어 | 408 B | 752 KiB | 816 KiB | +| 호출 중 슬롯, 계측 코어 | 480 B | 832 KiB | 896 KiB | + +같은 앱과 슬롯 배치에서 계측을 제외하면 컨트롤러는 **72 B/앱**, +측정된 RSS 증가는 1,000개에서 **80 KiB** 낮다. 이전 [049](049-idle-app-work-slots.md)의 +슬롯 절감도 **448 B/앱**, 이번 세 방식 비교에서는 RSS **448 KiB**로 분리된다. +RSS는 페이지 단위의 프로세스 관측값이며 구조체 바이트 수와 정확히 일치하지 않는다. +코어의 `text` 168 B 감소는 오브젝트 측정이고, 전체 앱 실행물·공유 라이브러리· +스택·저장 파일의 크기를 뜻하지 않는다. 계측을 빼면 HTTP 통계와 같은 누적 진단 +정보를 잃으므로, 그 호스트는 계속 선택형 계측을 링크한다. + +현재 소스에서 만든 [소스 편집 CLI](../../prototypes/connections/results/2026-09-24-compact-runtime/source-cli.tar.gz)는 +22,454 B, [고정 정의 CLI](../../prototypes/connections/results/2026-09-24-compact-runtime/cli.tar.gz)는 +12,885 B, [CLI+TUI](../../prototypes/connections/results/2026-09-24-compact-runtime/cli-tui.tar.gz)는 +31,971 B로 압축됐다. 각각 [소스 묶음](../../prototypes/connections/results/2026-09-24-compact-runtime/source-cli-check.json), +[고정 CLI](../../prototypes/connections/results/2026-09-24-compact-runtime/cli-check.json), +[TUI](../../prototypes/connections/results/2026-09-24-compact-runtime/cli-tui-check.json)를 +다른 경로에 풀어 실제 문서 작업으로 확인했다. 압축 크기는 실행 중 메모리와 다르며, +고정 정의 묶음에는 실행 시 `.pure` 수정 기능이 없다. +[기본 호스트](../../prototypes/connections/results/2026-09-24-compact-runtime/default-profile.json), +[고정 정의 호스트](../../prototypes/connections/results/2026-09-24-compact-runtime/fixed-profile.json), +[TUI](../../prototypes/connections/results/2026-09-24-compact-runtime/tui-profile.json)의 +링크된 코드·직접 의존성 검사도 각각 통과했다. 별도 [PTY 재검사](../../prototypes/connections/results/2026-09-24-compact-runtime/tui.json)의 +1 MiB 작업 피크 RSS는 3,168 KiB로 4,096 KiB 상한 이내였다. + +`make -C prototypes/connections check-portable check-core check-async`, `check-release`, +`check-async-server`, `check-tui`, `check-document-peer`가 통과했다. 비동기 HTTP는 +계측된 응답과 작업 회수도 확인했다. 현재 C 소스로 다시 링크한 scalar TLS 피어도 +상호 인증·충돌·재시작 검사를 통과했다. Linux AArch64의 결과이며 macOS 빌드나 +다수 앱의 처리량, 격리 수준을 증명하지 않는다. 이번 환경에는 Zig 0.15.2가 +없어 scalar 아카이브를 재생성하지 못했다. 작성된 C 정의가 현재 소스로 다시 +생성해도 동일한지 검사했고, 세 비교 빌드에 동일한 기존 scalar 아카이브를 사용했다. diff --git a/docs/research/052-clean-native-source-preview.md b/docs/research/052-clean-native-source-preview.md new file mode 100644 index 0000000..c7ff7cd --- /dev/null +++ b/docs/research/052-clean-native-source-preview.md @@ -0,0 +1,27 @@ +# 052 — 빌드 결과 없이 다시 만드는 네이티브 소스 묶음 + +첫 공개 체크포인트에는 이미 빌드된 Linux 실행물만이 아니라, 소스를 받은 사람이 +같은 문서 앱을 깨끗한 디렉터리에서 다시 빌드할 경로가 필요하다. 이번 묶음은 +마이크로 실행기의 기능이나 상주 메모리를 늘리지 않고 배포 경계만 만든다. + +[소스 묶음](../../prototypes/connections/results/2026-09-24-source-preview/pure-source-preview.tar.gz)은 +316,748 B로 압축됐고, C 실행 코어·문서 CLI/TUI/HTTP/TLS 호스트·테스트와 +Workbench 작성 소스를 포함한다. 과거 결과, 빌드 산출물, 캐시와 symlink는 +제외한다. 단, Workbench의 웹 예제를 위한 기존 `core.wasm`은 포함된다. +묶음 안의 `RUN.md`가 사용 명령을, `manifest.json`이 161개 파일 각각의 +크기와 SHA-256을 기록한다. gzip·tar 헤더의 시간과 파일 순서를 고정했고, +같은 소스에서 독립 생성한 세 묶음의 SHA-256은 모두 +`a11a058fa0ba71878a0fed0ee63100566f580721fa5cb602ad25cd46e6b94f6f`였다. + +[압축 해제 후 검사](../../prototypes/connections/results/2026-09-24-source-preview/source-check.json)는 +원본 작업 디렉터리의 빌드 결과를 참조하지 않는다. 임시 디렉터리에서 각 파일 +해시를 검증하고 `make check-portable`과 Linux `make check-release`를 실행했다. +소스 CLI가 실제 문서를 만들고, 새로 빌드한 고정 실행기가 같은 문서를 읽었으며, +`.pure`의 응답 규칙을 바꾼 뒤 새 문서의 응답이 달라졌다. 릴리스 검사에는 +로컬 HTTP 동작·코어/호스트 코드 크기·직접 의존성·CLI RSS 상한이 포함된다. + +이것은 **소스 개발 묶음**이다. 316,748 B는 실행 코어나 설치된 앱의 메모리가 +아니다. 고정 CLI 압축 실행물 12,885 B와도 기능·내용이 다르다. Linux AArch64 +깨끗한 빌드만 확인했다. Apple Silicon macOS의 컴파일·PTY·저장 동작은 아직 +실행하지 못했고, 소스 재배포 라이선스·서명·플랫폼별 설치 절차도 정해지지 +않았다. 따라서 공개 준비의 한 단계를 통과한 것이지 최종 공개 완료는 아니다. diff --git a/docs/research/053-source-loaded-native-terminal-editor.md b/docs/research/053-source-loaded-native-terminal-editor.md new file mode 100644 index 0000000..b57dd79 --- /dev/null +++ b/docs/research/053-source-loaded-native-terminal-editor.md @@ -0,0 +1,40 @@ +# 053 — 같은 문서 앱의 소스 정의를 읽는 네이티브 TUI + +기존 저메모리 TUI는 빌드 시 고정된 `editor.pure`만 실행했다. 정의를 바꿔 +실제로 써 보려면 소스 CLI 또는 큰 GTK 작성 창으로 옮겨야 했다. 선택형 +`source-tui`는 기존 터미널 편집기·저장 어댑터·실행 코어를 그대로 사용하며, +시작할 때 `--program FILE`로 `.pure`를 준비한다. CLI·TUI가 같은 정의와 +저장 문서·버전을 사용한다. 파일을 고친 뒤 **새 TUI 세션**을 열어야 바뀐 +정의가 적용된다. TUI 안에서 `.pure` 텍스트를 작성하거나 실행 중 정의를 +교체하는 기능은 아니다. + +이 TUI가 지원하는 정의에는 전체 문서 읽기와 서로 다른 두 u32le splice +필드, 뒤따르는 삽입 바이트가 필요하다. 맞지 않는 정의는 터미널 모드나 +저장소를 변경하기 전에 거절한다. [실제 PTY 검사](../../prototypes/connections/results/2026-09-24-source-tui/tui.json)에서는 +필드 순서를 바꾼 새 `.pure`를 읽어 두 번째 편집의 바이트 범위를 올바르게 +인코딩했다. 일반 정의에서는 한글·여러 행·탭·충돌 시 초안·크기 변경· +1 MiB 편집·1,024개 저장 문서 탐색과 터미널 복구를 재검증했다. + +| Linux AArch64 / GCC 14 | 고정 TUI | 소스 TUI | +|---|---:|---:| +| 링크된 `text` | [31,999 B](../../prototypes/connections/results/2026-09-24-source-tui/fixed-tui-profile.json) | [48,604 B](../../prototypes/connections/results/2026-09-24-source-tui/tui-profile.json) | +| CLI+TUI 압축 묶음 | [31,929 B](../../prototypes/connections/results/2026-09-24-source-tui/fixed-cli-tui.tar.gz) | [50,942 B](../../prototypes/connections/results/2026-09-24-source-tui/source-cli-tui.tar.gz) | +| 실행 중 `.pure` 파서 | 없음 | 있음 | + +소스 TUI의 실제 PTY 1 MiB 작업 피크 RSS는 이번 반복에서 3,216~3,664 KiB로 +기존 4,096 KiB 상한 안에 있었다. RSS는 실행마다 변동하므로 두 TUI의 +메모리 우열로 해석하지 않는다. [기본 실행 코어](../../prototypes/connections/results/2026-09-24-source-tui/core.json)의 5,908 B 코드 섹션과 +128 B 영역 구조체는 그대로이며, 소스 파서는 선택한 TUI 실행물에만 포함된다. + +[소스 CLI+TUI 묶음 검증](../../prototypes/connections/results/2026-09-24-source-tui/source-cli-tui-check.json)은 +다른 경로에 풀어 CLI의 수정된 `.pure` 응답과 TUI의 실제 PTY 문서 작업을 +확인했다. [고정 CLI+TUI 묶음](../../prototypes/connections/results/2026-09-24-source-tui/fixed-cli-tui-check.json)도 +별도로 이동 후 확인했다. 현재 162개 파일의 [소스 개발 묶음](../../prototypes/connections/results/2026-09-24-source-tui/pure-source-preview.tar.gz)은 +318,619 B이고, [깨끗한 디렉터리 검증](../../prototypes/connections/results/2026-09-24-source-tui/source-check.json)에서 +C 테스트·릴리스 검사·소스 TUI PTY 작업을 통과했다. 독립 재생성 해시도 +`6134ff966d09c76134b921008d16b828a349b7031bdf029379de6eea9e745eaa`로 일치했다. + +이 결과는 Linux AArch64의 문서 앱과 호환되는 `.pure` 정의에 한정된다. +macOS의 터미널·입력기·파일 저장은 검증하지 못했다. 소스 TUI의 편집 경험은 +범용 UI 작성기나 실시간 정의 교체를 대신하지 않으며, 소스 묶음의 크기도 +런타임 상주 메모리를 뜻하지 않는다. diff --git a/docs/research/054-direct-document-path.md b/docs/research/054-direct-document-path.md new file mode 100644 index 0000000..a5f72de --- /dev/null +++ b/docs/research/054-direct-document-path.md @@ -0,0 +1,53 @@ +# 054 — 실제 문서 작업의 직접 저장 경로와 Pure 경로 + +첫 공개 체크포인트에는 작은 코어 수치뿐 아니라 **같은 앱 작업을 직접 작성했을 때의 +전체 비용**이 필요하다. [재현 가능한 비교 구현](../../research/experiments/004-direct-document-path/README.md)은 +기존 `editor.pure`의 문서 생성→전체 읽기→마지막 1바이트 조건부 교체→전체 읽기를 +문서마다 실행한다. 두 실행물은 동일한 `store.c`를 링크하고 리비전·본문·결과 +해시를 검사한다. 각 쌍의 최종 저장 파일 바이트도 일치한다. 직접 경로는 저장 +함수를 호출하고, Pure 경로는 빌드 시 준비한 정의와 실행 코어·저장 호스트를 지난다. + +Linux AArch64, GCC 14 `-O2`, ext4 `/dev/mmcblk0p2`에서 저장 디렉터리를 +매번 새로 만들고 4쌍을 직접→Pure / Pure→직접 순서로 번갈아 실행했다. 각 쓰기는 +교체 파일과 디렉터리를 동기화하는 **동일한 저장소 코드**를 지난다. 먼저 +문서 4개로 무결성을 검사한 뒤 본 측정을 했다. `text`는 GNU `size`의 링크된 +코드 섹션이며, RSS는 Python 부모가 아닌 작은 네이티브 실행 부모가 수집한 +자식 프로세스의 피크다. + +| 작업 / 실행물 | 직접 호출 | Pure 경로 | +|---|---:|---:| +| 링크된 `text` | 10,364 B | 18,640 B | +| 링크된 `bss` | 16 B | 376 B | +| 256×8 KiB 문서, 1,024개 작업: 벽시계 중앙값 | 7.870 s | 7.601 s | +| 위 작업: 피크 RSS 중앙값 | 1,072 KiB | 1,088 KiB | +| 32×1 MiB 문서, 128개 작업: 벽시계 중앙값 | 8.929 s | 9.840 s | +| 위 작업: 피크 RSS 중앙값 | 4,368 KiB | 4,136 KiB | + +[8 KiB 원자료](../../research/experiments/004-direct-document-path/results/2026-09-24-ext4-8k-linux-aarch64.json)에서 +각 쌍의 Pure−직접 시간 차이는 +0.408, −0.408, +0.565, −0.379초다. +[1 MiB 원자료](../../research/experiments/004-direct-document-path/results/2026-09-24-ext4-1m-linux-aarch64.json)에서는 ++2.522, −4.239, +1.475, +10.147초다. 저장소 지연의 흔들림이 커서 +이 네 쌍으로 Pure의 속도 우열이나 평균 추가 비용을 확정할 수 없다. +1 MiB RSS도 실행별 직접 4,128~4,592 KiB, Pure 4,128~4,176 KiB로 +겹친다. 다만 **이 비교 실행물에서** Pure 경로는 +링크 코드 8,276 B와 정적 `bss` 360 B가 더 크다. 여기에는 준비된 정의, +평가기, 실행 코어, 저장 호스트, 계측용 작업 코드가 포함되므로 8,276 B를 +독립 코어의 크기나 모든 앱의 고정 오버헤드라고 부를 수 없다. + +이 작업의 지배적인 비용은 저장소에 있다. 8 KiB 프로필의 중앙값에서 +생성·교체 단계의 합은 직접 약 7.69초, Pure 약 7.58초로 각 경로의 +전체 시간 대부분이다. 읽기에는 본문 전체 비교와 FNV 해시도 포함한다. +커널·파일시스템 동작과 해시 비용이 작은 실행 분배 비용을 가리므로, +이 실험은 순수 디스패치 지연의 정밀 측정이 아니다. 앞선 `/tmp`의 +[8 KiB](../../research/experiments/004-direct-document-path/results/2026-09-24-tmpfs-8k-preliminary-linux-aarch64.json)· +[1 MiB](../../research/experiments/004-direct-document-path/results/2026-09-24-tmpfs-1m-preliminary-linux-aarch64.json) +탐색 결과는 tmpfs라서 영속 저장소 비교에 사용하지 않는다. + +직접 경로는 이 성공 입력의 결과만 맞춘 좁은 기준선이다. Pure의 예산, +소유권, 작업 완료·취소, 실패 전파, 동시 작업이나 RPC 보안까지 구현하지 +않았다. 따라서 같은 기능·보안을 갖춘 서버 비교, 네트워크 지연 비교, +실서비스 처리량 주장으로 확장할 수 없다. 다음 비교는 동일한 외부 +프로토콜·보안·오류 의미를 갖춘 피어에 같은 문서 작업을 주고, 본문 +소유권과 복사량, 메모리·지연의 비용을 함께 공개해야 한다. 그전까지 +마이크로 목표는 **기본 코어의 작은 수치와 필수 앱 전체 비용을 별개로 +판정**한다. diff --git a/docs/research/055-authenticated-peer-transport-cost.md b/docs/research/055-authenticated-peer-transport-cost.md new file mode 100644 index 0000000..b060594 --- /dev/null +++ b/docs/research/055-authenticated-peer-transport-cost.md @@ -0,0 +1,70 @@ +# 055 — 인증 문서 피어의 공통 전송 지연과 서버 전용 비용 + +같은 문서 앱의 직접 저장 비교 [054](054-direct-document-path.md)에 이어, 실제 +인증 RPC 경계까지 직접 기준선을 만들었다. [실험 구현·재현 절차](../../research/experiments/005-direct-document-peer/README.md)의 +두 서버는 같은 `editor.pure`, 프로그램 파서·평가기, 파일 저장소, +OpenSSL TLS 1.3, 상호 인증, ALPN, 요청·응답 형식을 사용한다. Pure 서버는 +작업 접수·수명·결과 대여를 실행 코어에 맡긴다. 직접 서버는 이 성공 +문서 작업을 수동으로 처리하므로 **일반 런타임 기능까지 동등한 기준선은 아니다.** + +처음에는 양쪽에서 인증된 8 KiB 문서 읽기 **32회에 약 1.412초**가 걸렸다. +요청당 약 44ms였다. [변경 전 원자료](../../research/experiments/005-direct-document-peer/results/2026-09-24-before-nodelay-8k-linux-aarch64.json)의 +중앙값은 직접 1,411.866ms, Pure 1,411.853ms다. 응답의 13B 머리와 +본문을 별도 TLS 쓰기로 보내는 공통 전송 경로에 `TCP_NODELAY`를 적용한 뒤, +[같은 32회 작업](../../research/experiments/005-direct-document-peer/results/2026-09-24-after-nodelay-8k-linux-aarch64.json)은 +직접 3.024ms, Pure 2.790ms였다. 같은 루프백 조건의 이 짧은 작업에서 +각각 약 467배·506배의 시간 감소다. TLS/TCP 경로에 있던 지연이 +Pure 디스패치 비용을 가렸다는 해석과 맞다. Nagle·지연 ACK의 구체적인 +패킷 상호작용은 캡처하지 않았으므로 **원인 메커니즘은 추론**이다. +이 배수를 원격망 처리량이나 범용 RPC 성능으로 내세우지 않는다. +`TCP_NODELAY`는 작은 TCP 패킷을 늘릴 수 있다. 패킷 수·원격망 지연·전력 +비용은 측정하지 않았으므로 이 선택의 범용 최적성도 아직 주장하지 않는다. + +수정 뒤 더 긴 실제 문서 읽기를 새 저장소·인증 연결에서 번갈아 실행했다. +Linux AArch64, GCC 14 `-O2`, OpenSSL 3.6.4, ext4 저장소의 루프백 결과다. +TLS 핸드셰이크·문서 생성·첫 읽기는 측정 밖이고, 매 요청의 8 KiB 또는 +1 MiB 본문 전체를 Python 클라이언트가 받았으며 결과를 확인했다. + +| 인증된 읽기 배치 | 직접 서버 중앙값 | Pure 서버 중앙값 | +|---|---:|---:| +| 8 KiB × 1,024회, 6쌍 | 84.436ms | 91.455ms | +| 위 배치의 요청당 시간 | 82.46µs | 89.31µs | +| 1 MiB × 64회, 4쌍 | 149.964ms | 152.685ms | +| 위 배치의 요청당 시간 | 2.343ms | 2.386ms | +| 벤치마크의 링크된 `text` | 37,862B | 43,454B | + +[8 KiB 원자료](../../research/experiments/005-direct-document-peer/results/2026-09-24-nodelay-8k-linux-aarch64.json)의 +쌍별 Pure−직접 시간은 +17.06, −2.19, +15.97, +3.42, −1.13, +−22.65ms다. [1 MiB 원자료](../../research/experiments/005-direct-document-peer/results/2026-09-24-nodelay-1m-linux-aarch64.json)는 ++0.75, −1.22, +18.14, +0.08ms다. 중앙값만 보면 Pure가 느리지만 +쌍별 편차와 역전이 있어 추가 지연의 안정적인 크기를 판정하지 않는다. +두 서버의 최종 저장 파일 SHA-256은 매 쌍에서 같았다. 서버 RSS는 +반복 읽기 뒤 대체로 약 8.6 MiB로 겹쳤다. OpenSSL·libc의 공유 매핑과 +프로세스 메모리를 포함하므로 전체 TLS 서버를 6 KiB 런타임이라고 +부를 수 없다. + +서버만 설치하는 경우를 위해 제품 피어에서 클라이언트 명령을 제외하는 +`make -C prototypes/connections document-server` 선택 빌드를 추가했다. +기존 클라이언트 겸용 빌드의 `text`는 46,934B, 서버 전용은 +[43,462B](../../research/experiments/005-direct-document-peer/pure-server-after-nodelay-profile-linux-aarch64.json)로 +3,472B 작다. 이 서버만의 [크기·의존성 상한](../../prototypes/connections/peer-server-budget.json)은 +선택 빌드에만 적용한다. 실험기가 별도 컴파일 명령으로 만든 43,454B와 +8B 차이가 있으므로 둘을 같은 실행물의 수치로 합치지 않는다. +기본 실행 코어와 CLI에는 TLS가 추가되지 않았다. +[서버 전용 이동 묶음과 크기 상한](056-server-only-document-package.md)도 +추가해, 같은 문서·인증 시나리오를 추출한 아카이브에서 재검증했다. + +[기존 클라이언트 겸용](../../research/experiments/005-direct-document-peer/pure-peer-after-nodelay-parity-linux-aarch64.json), +[서버 전용](../../research/experiments/005-direct-document-peer/pure-server-after-nodelay-parity-linux-aarch64.json), +[직접 서버](../../research/experiments/005-direct-document-peer/direct-peer-parity-linux-aarch64.json)의 +실제 인증·충돌·1 MiB·오류·복원 시나리오가 통과했다. +[직접 서버 ASan/UBSan 검사](../../research/experiments/005-direct-document-peer/direct-peer-sanitized-parity-linux-aarch64.json)와 +[Pure 서버 전용 ASan/UBSan 검사](../../research/experiments/005-direct-document-peer/pure-server-sanitized-parity-linux-aarch64.json)도 +통과했다. 공통 TLS 코드를 쓰는 기존 scalar 피어도 +[회귀 검사](../../research/experiments/005-direct-document-peer/scalar-peer-after-nodelay-parity-linux-aarch64.json)를 +통과했다. 이 검사는 기존 생성된 Zig 아카이브와 새 C 전송 코드를 다시 +링크한 것이며 Zig 전체 재생성은 아니다. 이는 현재 시나리오의 기능·메모리 +오류 증거이며, 여러 연결의 +공정성이나 장애망·공격 부하를 입증하지 않는다. 서버는 여전히 한 번에 +연결 하나만 처리하고, 5초 절대 요청 기한을 쓴다. macOS 실행, +실제 네트워크와 Kitex/QUIC 비교는 남아 있다. diff --git a/docs/research/056-server-only-document-package.md b/docs/research/056-server-only-document-package.md new file mode 100644 index 0000000..51ed32f --- /dev/null +++ b/docs/research/056-server-only-document-package.md @@ -0,0 +1,54 @@ +# 056 — 서버 전용 문서 피어를 실제 이동 묶음으로 검증 + +[055](055-authenticated-peer-transport-cost.md)의 서버 전용 실행물은 빌드와 +로컬 테스트까지만 확인했다. 이번에는 `package_native.py --with-peer-server`로 +CLI·`editor.pure`·인증 TLS 서버를 하나의 Linux AArch64 아카이브에 넣고, +다른 디렉터리에 풀어 실제 인증 문서 작업을 실행했다. 서버만의 배포 +구성에서도 외부 통신을 유지한다. `--with-peer`는 기존의 C 클라이언트 +겸용 구성을 그대로 선택한다. 두 옵션은 동시에 사용할 수 없다. + +```sh +python3 prototypes/connections/package_native.py --with-peer-server \ + --output dist/pure-document-server-only-linux-aarch64.tar.gz +python3 prototypes/connections/check_package.py \ + dist/pure-document-server-only-linux-aarch64.tar.gz +``` + +| 같은 CLI·정의와 TLS 피어 하나를 포함한 구성 | 서버 전용 | 클라이언트 겸용 | +|---|---:|---:| +| 압축 아카이브 전체 | [47,686B](../../prototypes/connections/results/2026-09-24-server-only-package/pure-document-server-only-linux-aarch64.tar.gz) | [49,382B](../../prototypes/connections/results/2026-09-24-server-only-package/pure-document-combined-peer-linux-aarch64.tar.gz) | +| 선택 피어의 링크된 `text` | 43,462B | 46,934B | +| 검토한 아카이브 상한 | 51,200B | 53,248B | + +동일한 선택 범위에서 서버 전용 묶음은 **1,696B 작다**. 이 차이는 압축 +파일 비용이며 프로세스 RAM 절감량이 아니다. [새 아카이브 예산](../../prototypes/connections/peer-package-budget.json)은 +CLI와 TLS 피어만 담은 두 구성에 적용된다. HTTP·TUI·GTK 또는 연구용 +저장소까지 고른 조합에는 이 상한을 적용하지 않는다. 기존의 실행물 +`text`·data·BSS와 직접 동적 의존성 검사도 별도로 유지한다. +서버 전용 묶음을 다른 출력 경로에 다시 생성해 SHA-256 +`34fb507c160e4aa719cf44fb4f8bbcb422d2c0ee6dc3f206f6a5fe3bc4d089b6`가 +같음을 확인했다. 현재 TLS 피어와 연구용 페이지 저장소를 동시에 선택하면 +저장 형식이 다를 수 있으므로 패키저가 그 조합을 거절한다. + +[서버 전용 이동 검증](../../prototypes/connections/results/2026-09-24-server-only-package/server-only-package-check.json)은 +manifest와 모든 파일의 SHA-256을 확인하고, 추출한 CLI의 문서 생성·복원과 +수정한 `.pure`의 응답 변화를 실행했다. 같은 추출 경로의 서버에는 패키지 +밖의 Python TLS 클라이언트가 접속했다. 잘못된 클라이언트 인증서·서버 +호스트 이름·ALPN을 거부했고, 조건부 생성·충돌·1 MiB 분할 수신·응답 +본문 생략·마지막 1바이트 변경을 처리했다. 잘린 프레임과 잘못된 연산은 +저장값을 바꾸지 않았으며 서버 재시작 뒤에도 1 MiB 값을 읽었다. 패키지 +안 CLI도 같은 값을 읽었다. 이 검사의 서버 `/proc` RSS 관찰값은 연결 전 +7,968 KiB, 큰 작업 뒤 8,672 KiB다. 피크나 다른 장치의 메모리 상한은 +아니다. + +[클라이언트 겸용 이동 회귀](../../prototypes/connections/results/2026-09-24-server-only-package/combined-peer-package-check.json)는 +기존 C/Python TLS·1 MiB 작업과 재시작까지 통과했다. 두 아카이브의 +manifest에는 선택 호스트, 파일 해시, 시스템 공유 라이브러리의 직접 +의존성 및 검토한 이진·아카이브 상한이 들어 있다. OpenSSL과 libc를 +묶음 안에 넣지 않으며, 인증서도 사용자가 준비해야 한다. 서버 전용 +묶음에는 C `call` 명령이 없다. 외부 클라이언트는 `pure-doc/1` 형식을 +구현하거나 별도의 Pure 클라이언트를 사용한다. + +이 결과는 이동 가능한 Linux AArch64 패키지의 한 선택 구성을 증명한다. +macOS/Apple Silicon 실행, 서명·설치 경험, 다중 연결 공정성, 장시간 부하, +장애망 복구와 전체 공개 체크포인트는 아직 완료되지 않았다. diff --git a/docs/research/057-portable-native-terminal-check.md b/docs/research/057-portable-native-terminal-check.md new file mode 100644 index 0000000..116339e --- /dev/null +++ b/docs/research/057-portable-native-terminal-check.md @@ -0,0 +1,45 @@ +# 057 — 같은 네이티브 문서 TUI의 휴대형 PTY 검증 경로 + +첫 공개 체크포인트는 사용자가 주로 쓰는 Apple Silicon macOS에서 네이티브 +앱이 실행되어야 한다. 지금 작업 호스트는 Linux AArch64라서 macOS 실행을 +증명할 수 없다. 기존 `check-portable`은 C 단위 동작만 검사했고, 실제 +TUI 검사는 Linux `/proc` RSS를 반드시 읽었다. 이에 동일한 TUI 시나리오를 +유지하되 `/proc` 수치를 명시적으로 제외하는 `check-portable-tui` 경로를 +추가했다. 고정 정의와 소스 정의 TUI를 모두 실제 PTY에서 실행한다. + +```sh +make -C prototypes/connections check-portable +make -C prototypes/connections check-portable-tui +``` + +검사기는 설치된 UTF-8 로케일을 골라 자식 터미널에 지정한다. 휴대형 모드의 +RSS 필드는 `null`, 상한은 0이며 결과에 `portable: true`를 기록한다. +기존 Linux 검사에서는 `/proc` 피크와 4,096 KiB 상한을 그대로 확인한다. + +| Linux AArch64에서 실제 실행한 경로 | 확인한 내용 | +|---|---| +| [고정 TUI 휴대형 PTY](../../prototypes/connections/results/2026-09-24-portable-tui/fixed-linux-aarch64.json) | 입력·다른 문서·한글 이동·크기 변경·충돌·1 MiB 편집·1,024개 목록·종료 후 터미널 복구 | +| [소스 TUI 휴대형 PTY](../../prototypes/connections/results/2026-09-24-portable-tui/source-linux-aarch64.json) | 위 작업과 잘못된 정의 거부, `.pure` 필드 배치를 바꾼 뒤의 실제 편집 | +| [고정 TUI Linux RSS 회귀](../../prototypes/connections/results/2026-09-24-portable-tui/fixed-linux-rss-regression.json) | 1 MiB 작업 피크 3,168 KiB, 기존 코드·메모리 상한 통과 | +| [소스 TUI Linux RSS 회귀](../../prototypes/connections/results/2026-09-24-portable-tui/source-linux-rss-regression.json) | 1 MiB 작업 피크 3,664 KiB, 기존 코드·메모리 상한 통과 | + +새 [소스 묶음](../../prototypes/connections/results/2026-09-24-portable-source/pure-source-preview.tar.gz)은 +165개 파일, 압축 321,399B다. SHA-256은 +`5f1766fefe2c319d86c3ff57c881638a9d1ee24374dea33c614e25577d087814`이며 +다른 출력 경로에 다시 만든 묶음과 일치했다. [깨끗한 디렉터리 검사](../../prototypes/connections/results/2026-09-24-portable-source/source-check-linux-aarch64.json)는 +manifest의 모든 파일 해시, C17 검사, 고정·소스 TUI PTY 작업, 실제 문서 +작성·규칙 변경, Linux 릴리스 크기·RSS 검사를 통과했다. +321,399B는 개발용 소스 배포 크기이며 실행물 크기나 상주 메모리가 아니다. +이번 변경은 Python 검사기·Make·문서에 한정되어 C 런타임 실행 경로를 +키우지 않았다. 기본 코어와 선택형 TUI의 코드·RSS 상한은 각각 기존 +`check-release`, `check-tui`, `check-source-tui`로 분리해 검사한다. + +macOS에서 검증할 때는 이 소스 묶음을 풀어 위 두 `make` 명령을 실행해야 +한다. `check-portable-tui`가 macOS에서 통과했다는 결과는 **아직 없다**. +PTY 시나리오도 IME 조합 입력이나 GUI 접근성 검사가 아니다. macOS 저장소의 +실제 APFS 동작과 전원 장애 내구성 역시 별도로 검증해야 한다. 현재 저장 +어댑터는 파일·디렉터리에 `fsync`를 사용한다. Apple의 +[fsync(2) 설명](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fsync.2.html)은 +드라이브 캐시까지 포함한 강한 쓰기 순서를 보장하려면 `F_FULLFSYNC`를 +구분해야 한다고 설명한다. 따라서 이 Linux 검사를 macOS의 강한 영속성 +증거로 해석하지 않는다. diff --git a/docs/research/058-portable-authenticated-peer-check.md b/docs/research/058-portable-authenticated-peer-check.md new file mode 100644 index 0000000..0928468 --- /dev/null +++ b/docs/research/058-portable-authenticated-peer-check.md @@ -0,0 +1,38 @@ +# 058 — 선택형 인증 피어의 휴대형 실작업 검사 + +첫 공개 체크포인트에서는 Pure 간 연결이 같은 앱 정의와 문서 작업을 실제로 +교환해야 한다. 기존 인증 피어 검사는 서버의 `/proc` RSS를 읽어 Linux 외 +환경에서 시작할 수 없었다. `check-portable-peer`는 그 수집만 제외하고 +같은 TLS 클라이언트·서버 작업을 실행한다. 실행 코어와 C 피어 코드는 +변경하지 않았고, TLS는 여전히 선택한 빌드에만 링크된다. + +```sh +make -C prototypes/connections check-portable-peer +``` + +OpenSSL 헤더·라이브러리·명령행 도구와 Python 3가 필요하다. 검사기는 임시 +인증서를 만들고 실제 루프백 소켓에서 상호 인증, 서버 이름·ALPN·앱 소스 해시 +불일치, C 피어와 외부 Python 클라이언트의 문서 읽기·편집·충돌·복구, 조각난 +1 MiB 요청, 잘못된 프레임, 연결 기한, 서버 재시작 후 복원을 확인한다. + +| Linux AArch64 실제 실행 | 결과 | +|---|---| +| [범용 피어 휴대형 검사](../../prototypes/connections/results/2026-09-24-portable-peer/client-server-linux-aarch64.json) | 9개 실작업 시나리오 통과, RSS 미수집 | +| [서버 전용 휴대형 검사](../../prototypes/connections/results/2026-09-24-portable-peer/server-only-linux-aarch64.json) | 같은 9개 시나리오 통과, RSS 미수집 | +| [범용 피어 Linux 회귀](../../prototypes/connections/results/2026-09-24-portable-peer/client-server-linux-rss-regression.json), [코드 경계](../../prototypes/connections/results/2026-09-24-portable-peer/client-server-profile.json) | `text` 46,934 B, 유휴 7,968 KiB·1 MiB 작업 후 9,712 KiB RSS | +| [서버 전용 Linux 회귀](../../prototypes/connections/results/2026-09-24-portable-peer/server-only-linux-rss-regression.json), [코드 경계](../../prototypes/connections/results/2026-09-24-portable-peer/server-only-profile.json) | `text` 43,462 B, 유휴 7,968 KiB·1 MiB 작업 후 9,712 KiB RSS | + +새 [소스 묶음](../../prototypes/connections/results/2026-09-24-portable-peer-source/pure-source-preview.tar.gz)은 +165개 파일, 압축 321,958 B이며 SHA-256은 +`bf51f79c400f820b48dbf78522449cda6625563ce860809e86d76a8023377dbb`다. +[이동 후 검사](../../prototypes/connections/results/2026-09-24-portable-peer-source/source-check-linux-aarch64.json)는 +manifest 해시·C 동작·두 TUI의 실제 PTY 작업·변경된 `.pure` 결과·선택형 +TLS 두 서버·Linux 릴리스 크기와 RSS 상한을 깨끗한 디렉터리에서 통과했다. +압축 소스 묶음 크기는 실행 중 메모리가 아니다. TLS 서버의 위 RSS에는 +선택한 OpenSSL 공유 라이브러리와 실제 작업 비용이 포함되며, 기본 실행 +코어의 크기를 대신하지도 않는다. + +Apple Silicon macOS에서 이 명령을 컴파일하거나 실행한 증거는 아직 없다. +특히 macOS의 OpenSSL 헤더·라이브러리 경로와 저장소 디렉터리 동기화는 +별도 검증이 필요하다. 휴대형 결과의 RSS 필드는 `null`이며 Linux 메모리 +측정을 대체하지 않는다. diff --git a/docs/research/059-immutable-file-copy-path.md b/docs/research/059-immutable-file-copy-path.md new file mode 100644 index 0000000..42bff3a --- /dev/null +++ b/docs/research/059-immutable-file-copy-path.md @@ -0,0 +1,57 @@ +# 059 — 큰 불변 문서의 바뀌지 않은 구간을 복사하는 비용 + +기본 저장소는 문서 전체를 불변 파일로 다시 공개한다. 1 MiB 문서의 마지막 +1바이트를 바꿀 때 기존 바이트를 4 KiB 스택 버퍼로 읽고 새 파일에 쓴다. +[재현 검사](../../prototypes/connections/check_copy_path.py)는 같은 +`editor.pure` CLI로 문서를 만든 뒤 실제 마지막 바이트를 수정하고, 별도 +프로세스에서 최종 1 MiB 본문을 대조한다. 저장 디렉터리는 작업트리의 +ext4에 만들고 `findmnt`로 파일시스템을 기록한다. Linux `strace`로 객체 파일의 +성공한 호출을 세고 작은 네이티브 부모의 `wait4`로 자식 피크 RSS를 잰다. + +선택형 `PURE_STORE_CFR_COPY` 경로는 길이 16 KiB 이상의 바뀌지 않은 +구간에 Linux `copy_file_range()`를 먼저 시도한다. 짧은 복사는 반복하고, +지원되지 않는다고 보고되면 남은 오프셋부터 기존 읽기·쓰기로 이어간다. +다른 저장 형식을 만들지 않으며, 기본 빌드와 macOS 빌드에는 들어가지 않는다. +부분 복사 뒤 미지원 오류를 강제로 주입한 C 저장 검사도 통과했다. + +| 같은 1 MiB 문서의 마지막 1 B 수정 | 기본 | 선택형 Linux 복사 | +|---|---:|---:| +| 원본 객체 `pread64` 호출 | 257 | 1 | +| `copy_file_range` 호출·전달 바이트 | 0 | 1회·1,048,575 B | +| 임시 객체 `write` 호출·바이트 | 258회·1,048,600 B | 2회·25 B | +| 이 작업의 자식 피크 RSS | 1,056 KiB | 1,056 KiB | +| CLI 링크 `text` | 35,783 B | 36,052 B | +| HTTP 호스트 링크 `text` | 43,938 B | 44,207 B | + +[실제 복사 추적·본문 해시·RSS](../../prototypes/connections/results/2026-09-24-copy-range/copy-path.json), +[기본 실행물 비용](../../prototypes/connections/results/2026-09-24-copy-range/default-profile.json), +[선택 실행물 비용](../../prototypes/connections/results/2026-09-24-copy-range/profile.json). +선택에 따른 링크 `text` 증가는 CLI·HTTP에서 각각 **269 B**다. +[기본 코어](../../prototypes/connections/results/2026-09-24-copy-range/default-core.json)의 +`text` 5,908 B·영역 128 B는 바뀌지 않는다. 선택형 CLI의 1 MiB 문서 생성 +[RSS 회귀](../../prototypes/connections/results/2026-09-24-copy-range/memory.json)도 +4 MiB 상한 안이다. 위 1,056 KiB는 기존 문서 수정 한 번의 피크이며, +생성·출력을 합친 전체 앱 피크와 혼동하지 않는다. + +이 결과는 **사용자 공간 읽기·쓰기 호출 수**의 감소다. 전달된 1,048,575 B가 +사라진 것은 아니다. 파일시스템이 데이터를 실제로 복사할지 공유할지는 구현에 +달렸고, 물리 디스크 쓰기량·지연 시간의 개선은 증명하지 않았다. Linux +[copy_file_range(2) 설명](https://man7.org/linux/man-pages/man2/copy_file_range.2.html)은 +부분 복사와 파일시스템별 구현 및 오래된 커널의 일부 가상 파일시스템 결함을 +명시한다. 따라서 이 경로는 **현재 Linux 6.18/ext4에서 검증한 선택형 연구 +경로**로 남긴다. 다른 커널·파일시스템의 성공 반환이 실제 본문과 일치하는지는 +별도로 검증해야 한다. + +[실제 앱 기능 검사](../../prototypes/connections/results/2026-09-24-copy-range/functional.json), +[8클라이언트·1,024문서 검사](../../prototypes/connections/results/2026-09-24-copy-range/service-scale.json)는 +최종 문서와 프로세스 상한을 통과했다. 다만 규모 검사의 문서는 8 KiB이므로 +이 선택형 16 KiB 이상 복사 경로의 **성능 검사로 해석하지 않는다**. +기본 경로의 [전체 릴리스 검사](../../prototypes/connections/results/2026-09-24-copy-range/default-functional.json)도 +통과했다. 재현 명령은 `make -C prototypes/connections check-copy-range`다. + +최종 [소스 묶음](../../prototypes/connections/results/2026-09-24-copy-range-source/pure-source-preview.tar.gz)은 +166개 파일, 압축 324,261 B, SHA-256 +`ce48ef8e330cff470b78aec9c9e6c6d8877e4c8c371512f190b91b6036ddc8ba`다. +[깨끗한 디렉터리 검사](../../prototypes/connections/results/2026-09-24-copy-range-source/source-check-linux-aarch64.json)에서 +C 동작·두 TUI의 PTY 편집·선택형 TLS 피어·파일 간 복사·Linux 릴리스 +상한을 모두 통과했다. 이 압축량은 소스 배포 비용이지 상주 메모리가 아니다. diff --git a/docs/research/060-host-native-checkpoint.md b/docs/research/060-host-native-checkpoint.md new file mode 100644 index 0000000..88caf0f --- /dev/null +++ b/docs/research/060-host-native-checkpoint.md @@ -0,0 +1,24 @@ +# 호스트 네이티브 체크포인트: 실행 비용은 앱 작업에서 측정 + +기본 C 코어와 실행물에는 기능을 추가하지 않았다. 개발용 `check-native-checkpoint` +명령이 기존 C 동작 검사와 실제 PTY 편집 검사를 실행하고, 고정 정의 및 소스 정의 +CLI 각각에서 1 MiB 문서 생성·읽기·1바이트 교체·새 프로세스 복원을 검사한다. +`measure-peak-rss`는 `wait4`로 자식 프로세스의 최대 RSS를 수집한다. +현재 [Apple XNU `getrusage(2)` 설명](https://github.com/apple-oss-distributions/xnu/blob/main/bsd/man/man2/getrusage.2)은 +`ru_maxrss`를 바이트로, Linux는 KiB로 정의한다. 도우미는 원시 값·단위를 함께 +남기고 KiB로 정규화한다. 플랫폼 사이에 같은 메모리 상한을 적용하지 않는다. + +2026-09-24 Linux AArch64의 새 빌드 `build/native-checkpoint`에서 C·고정/소스 +TUI PTY 검사가 통과했다. [소스 CLI 결과](../../prototypes/connections/results/2026-09-24-native-checkpoint/source-memory.json)는 +1 MiB 생성 2,080 KiB, 1바이트 수정 1,104 KiB 피크 RSS였다. +[고정 CLI 결과](../../prototypes/connections/results/2026-09-24-native-checkpoint/fixed-memory.json)는 +각각 2,128 KiB와 1,104 KiB였다. 두 결과 모두 디버그 심볼을 포함한 실행 파일 +크기와 SHA-256을 기록한다. 각 [고정 TUI](../../prototypes/connections/results/2026-09-24-native-checkpoint/fixed-tui.json)와 +[소스 TUI](../../prototypes/connections/results/2026-09-24-native-checkpoint/source-tui.json)의 +portable 검사는 실제 PTY 동작을 확인하지만 RSS는 기록하지 않는다. +기존 Linux 전용 `check-micro-memory` 상한도 재검사해 통과했다. + +이 수치는 프로세스 피크이며 파일 시스템 캐시·커널 버퍼·선택형 TLS/GUI 비용을 +포함하지 않는다. 지연이나 디스크 물리 쓰기 개선을 뜻하지 않는다. Apple Silicon +macOS에서는 아직 컴파일·실행하지 못했다. 소스 묶음에서 명령을 실행한 뒤 +그 호스트의 실제 결과를 받아야 네이티브 호환성과 메모리 비용을 판단할 수 있다. diff --git a/docs/research/061-durable-document-edit-latency.md b/docs/research/061-durable-document-edit-latency.md new file mode 100644 index 0000000..e458d46 --- /dev/null +++ b/docs/research/061-durable-document-edit-latency.md @@ -0,0 +1,54 @@ +# 061 — 같은 불변 문서 편집의 완료 시간과 페이지 재구성 비용 + +큰 문서의 1바이트 수정에서 기본 불변 파일, Linux `copy_file_range` 선택 경로, +16 KiB 페이지 선택 저장소를 **같은 `editor.pure` 소스 CLI**로 비교했다. +비교 코드는 [단일 수정](../../research/experiments/005-storage-edit-latency/measure.py)과 +[연속 수정](../../research/experiments/005-storage-edit-latency/sequence.py)에 있다. +각 저장소에서 1 MiB 문서를 만들고, 새 CLI 프로세스로 마지막 1바이트를 리비전 +검사와 함께 바꾼다. 생성·수정은 각각 `fsync`를 포함한다. 이후 다른 프로세스의 +`show`로 전체 본문과 리비전을 확인한다. 측정값은 CLI 시작·`.pure` 준비· +저장소 초기화까지 포함하고 검증용 `show` 시간은 제외한다. + +2026-09-24 Linux AArch64, ext4 `/dev/mmcblk0p2`에서 각 방식 18회씩, +실행 순서를 순환해 측정했다. [모든 원자료](../../research/experiments/005-storage-edit-latency/2026-09-24-ext4-linux-aarch64.json)에 +실행물 해시·매회 완료 시간·최종 본문 해시·파일 할당량이 있다. + +| 실제 CLI 작업의 중앙값 | 기본 불변 파일 | 선택형 파일 간 복사 | 선택형 페이지 | +|---|---:|---:|---:| +| 1 MiB 새 문서 생성 | 83.556 ms | 81.672 ms | 88.948 ms | +| 기존 문서 마지막 1 B 수정 | 78.805 ms | 77.908 ms | 14.473 ms | +| 수정 후 파일 할당량 | 1,052,672 B | 1,052,672 B | 1,069,056 B | +| CLI 링크 `text` | 35,783 B | 36,052 B | 41,040 B | + +별도 자식 프로세스 RSS 검사에서 1 MiB 생성 피크는 기본 2,080 KiB, +파일 간 복사 2,080 KiB, 페이지 2,112 KiB였다. 1바이트 수정 피크는 +각각 1,104·1,056·1,056 KiB였다. 이 한 번씩의 값은 작은 차이의 우열을 +판정하지 못하지만 세 방식 모두 같은 규모의 전체 CLI 메모리를 사용했다. +[기본](../../research/experiments/005-storage-edit-latency/flat-memory.json), +[파일 간 복사](../../research/experiments/005-storage-edit-latency/copy-memory.json), +[페이지](../../research/experiments/005-storage-edit-latency/paged-memory.json)의 +원시 단위·실행물 해시를 함께 남겼다. + +페이지 경로는 같은 회차의 기본 경로보다 16/18회, 파일 간 복사보다 17/18회 +짧았다. 그러나 한 번은 565.5 ms로 길었다. 파일 간 복사는 사용자 공간 호출을 +줄이지만, 이 환경의 완전한 내구성 편집 시간에서는 기본 경로와 구분되는 +중앙값 이득을 보이지 않았다. 이를 보편적인 처리량 결과로 확장하지 않는다. + +한 문서를 70번 연속 수정하는 [원자료](../../research/experiments/005-storage-edit-latency/2026-09-24-sequence-ext4-linux-aarch64.json)에서는 +기본 파일 83.176 ms, 페이지 저장소 18.858 ms가 중앙값이다. 페이지가 같은 +수정 회차에서 더 짧은 경우는 59/70회였다. 64회차까지 페이지 세그먼트의 +할당량이 2,101,248 B로 증가했고, **65회차에 재구성**되며 1,052,672 B로 +내려갔다. 이번 재구성은 35.067 ms였으나 다른 회차에 219.485 ms 지연이 +발생했다. 즉, 재구성만이 긴 지연의 원인은 아니다. 저장 장치의 캐시·동기화· +백그라운드 작업을 통제하지 않았으므로 꼬리 지연 상한은 제시할 수 없다. + +현재 선택형 페이지 저장소의 [전체 검사 기록](../../research/experiments/005-storage-edit-latency/2026-09-24-check-hybrid.log.gz)은 +앱 동작, 충돌·스냅샷·실패·재구성 C 검사와 Linux 코드·RSS 상한을 통과했다. +페이지 저장소의 링크 `text`는 기본 CLI보다 **5,257 B 크다**. 기본 코어는 +변경하지 않았다. 또한 기존 기본 저장소는 새 페이지 형식을 직접 읽지 못하고, +선택형 저장소는 OS의 16 KiB 페이지 크기를 요구한다. 이후 +[오프라인 형식 내보내기](062-paged-store-rollback.md)는 확인했지만 macOS 실행과 +운영 중 투명한 전환은 검증하지 못했다. 따라서 지금은 페이지 저장을 **선택형 연구 +프로필**로 유지하고, 이 실측 결과를 형식 전환·복구 설계의 우선순위 근거로 +사용한다. 작은 문서의 쓰기량, 충돌, 오래된 읽기와 실패 계약은 +[기존 하이브리드 기록](034-hybrid-document-storage.md)에 있다. diff --git a/docs/research/062-paged-store-rollback.md b/docs/research/062-paged-store-rollback.md new file mode 100644 index 0000000..1f734dc --- /dev/null +++ b/docs/research/062-paged-store-rollback.md @@ -0,0 +1,45 @@ +# 062 — 선택형 페이지 저장소를 기본 불변 형식으로 되돌리기 + +[061](061-durable-document-edit-latency.md)의 페이지 저장소는 실제 큰 문서 +수정에서 짧은 중앙값을 보였지만, 기본 저장기가 `PUREP001`을 읽지 못했다. +이 형식 경계를 줄이기 위해 [문서 앱 전용 내보내기](../../prototypes/connections/export_flat_store.py)를 +만들었다. 도구는 **새 디렉터리**에 현재 본문과 리비전을 `PUREB001`로 기록하고, +기본 고정 CLI로 전부 재읽은 후에 디렉터리를 공개한다. 원본을 변경하지 않는다. +런타임 코어와 기본 앱의 C 소스·링크 경로에는 코드가 추가되지 않았다. + +변환 동안 원본 `.writer.lock`의 공유 POSIX 잠금을 유지한다. 협력하는 쓰기 +프로세스는 `busy`를 받으며, 키 목록과 각 읽기는 한 시점의 일관된 상태를 본다. +각 대상 파일과 대상 디렉터리를 동기화하고, 임시 디렉터리를 최종 경로로 +이름 변경한 뒤 상위 디렉터리도 동기화한다. 중간 읽기가 실패하면 임시 +디렉터리를 지우고 최종 경로를 공개하지 않는다. 대상 경로는 실행 전에 +존재하지 않아야 하며, 그 경로를 다른 프로세스가 동시에 생성하지 않아야 한다. +서비스 쓰기가 `busy`를 받지 않도록 유지보수 중 실행하는 경로다. + +[실제 앱 검사](../../prototypes/connections/results/2026-09-24-flat-export/export-check.json)는 +빈 값·1바이트·16 KiB 경계·16 KiB 초과·1 MiB 문서를 생성하고, 큰 문서를 +65회 수정한 후 내보냈다. 모든 본문과 리비전이 새 기본 저장기에서 일치했고, +기본 저장기로 이어서 수정하자 리비전이 66→67로 증가했다. 원본은 66을 +유지했다. 변환 중 별도 쓰기는 `busy`, 종료 뒤 같은 쓰기는 성공했다. +기존 대상·심볼릭 링크 객체·주입한 중간 읽기 실패도 거절하고 미완성 +대상을 남기지 않았다. + +[1,024개 추가 문서 검사](../../prototypes/connections/results/2026-09-24-flat-export/export-scale-1024.json)는 +총 1,029개 객체, 1,212,418 논리 바이트를 변환하고 각 객체를 기본 CLI로 +재읽었다. 이는 `/tmp` 파일시스템의 기능 검사이며 처리량 자료가 아니다. +[ext4 경로 검사](../../prototypes/connections/results/2026-09-24-flat-export/export-ext4.json)는 +같은 다섯 문서와 동기화·공개·복원을 작업트리의 ext4에서 실행했다. +재현 명령은 `make -C prototypes/connections check-flat-export`다. 현재 +`check-hybrid`에도 이 검사가 포함된다. + +[새 소스 묶음](../../prototypes/connections/results/2026-09-24-flat-export-source/pure-source-preview.tar.gz)은 +168개 파일, 압축 329,118 B이며 [별도 디렉터리 검사](../../prototypes/connections/results/2026-09-24-flat-export-source/source-check-linux-aarch64.json)에서 +이 변환, C·TUI·CLI 동작과 Linux 릴리스 상한을 통과했다. 앞선 묶음과 +해시를 비교하면 실행용 C·헤더는 모두 같다. 새 코드는 선택형 Python +변환기·검사와 사용 안내에만 있다. + +도구는 번들된 `editor.pure` 문서 앱의 고정 CLI를 사용하며 범용 저장소 +마이그레이터는 아니다. 소스가 16 KiB 페이지를 요구하고 Apple Silicon에서는 +아직 컴파일·실행하지 않았다. 원본을 제자리에서 바꾸거나 실행 중 쓰기를 +투명하게 이전하지 않으며, 파일 손상 복구나 모든 전원 장애를 증명하지 않는다. +기본 저장기는 여전히 페이지 파일을 직접 읽지 못한다. 이 경로는 선택형 +페이지 실험을 되돌릴 실제 수단이지, 기본 저장 경로 승격의 근거 전체는 아니다. diff --git a/docs/research/063-real-in-process-routing.md b/docs/research/063-real-in-process-routing.md new file mode 100644 index 0000000..24296c5 --- /dev/null +++ b/docs/research/063-real-in-process-routing.md @@ -0,0 +1,47 @@ +# 063 — 실제 문서 하나를 같은 프로세스의 여러 실행 영역으로 전달 + +Pure의 출력이 다른 입력이 되는 코어 계약을 실제 앱 데이터로 연결했다. +선택형 [네이티브 `pure-mirror`](../../prototypes/connections/mirror.c)는 +빌드 시 준비한 [`storage.pure`](../../prototypes/connections/examples/storage.pure)를 +사용한다. 원본 저장소·각 대상 저장소는 같은 프로세스의 독립된 `PureRuntime` +영역이다. 원본 문서를 한 번 읽어 매핑하고 `pure_runtime_forward`로 최대 8개 +대상에 전달한다. 대상 호스트의 입력 할당기는 `NULL`로 두었다. 원본 호출자의 +티켓을 대상 실행 전에 해제해도 코어가 매핑을 유지하며, 마지막 대상 완료 후 +영역의 보유 바이트와 작업 슬롯이 0으로 돌아오는 것을 호스트가 확인한다. +대상 파일을 영속화하는 쓰기는 각각 수행한다. **절약한 것은 그 사이의 추가 +입력 버퍼·직렬화**, 전체 저장 바이트나 디스크 쓰기가 아니다. + +[실제 ext4 검사](../../prototypes/connections/results/2026-09-24-in-process-routing/mirror-ext4.json)는 +`editor.pure`로 만든 1 MiB 문서를 두 영역에 전달해 각 최종 본문·리비전을 +새 CLI에서 검사했다. 원본을 한 바이트 수정해도 두 대상은 이전 스냅샷을 +유지했다. 다음 전달에서 한 대상은 리비전 2로 성공하고 다른 대상은 잘못된 +예상 리비전으로 충돌했다. 성공을 되돌리지 않고 대상별 결과를 보고하며, +충돌 대상은 올바른 리비전으로 다시 전달해 복구했다. 두 번째 대상의 키가 +잘못되면 모든 대상의 접수가 끝나기 전에 문서 쓰기를 시작하지 않았다. 빈 문서와 +원본 없음도 확인했다. 최대 8개 대상에도 1 MiB 본문·리비전이 모두 일치했다. +대상 예산을 1,024 B로 제한하면 1 MiB 전달은 문서 쓰기 전에 거절됐고, +예산이 0인 대상에는 빈 문서가 전달됐다. +[같은 시나리오의 임시 저장소 검사](../../prototypes/connections/results/2026-09-24-in-process-routing/mirror-tmp.json)도 통과했다. + +Linux AArch64에서 이 선택형 명령의 링크된 `text`는 **18,538 B**였다. +ext4의 1 MiB 문서를 두 저장소로 전달한 한 실행의 **전체 프로세스 피크 RSS는 +2,144 KiB**였고, 같은 실행기에서 8개 저장소로 전달한 샘플도 2,144 KiB였다. +대상별 예산 장부는 각각 1 MiB를 기록하지만 같은 원본 매핑의 크기를 물리 +메모리로 8번 더하지 않는다. 이 수치에는 libc와 파일 쓰기가 포함되지만 페이지 캐시, +커널 버퍼와 다른 앱은 포함되지 않는다. 기본 실행 코어·기본 CLI 코드에는 +변경이 없다. [ASan·UBSan 실행](../../prototypes/connections/results/2026-09-24-in-process-routing/mirror-sanitized.json)도 +같은 기능 검사를 통과했다. 이 샌드박스에서 LeakSanitizer는 ptrace 때문에 +실행되지 않아 누수 탐지는 그 검사에 포함되지 않는다. + +재현 명령은 `make -C prototypes/connections check-mirror`다. 이 경로는 +**프로세스 내 전달과 수명**의 실제 앱 증거다. 대상 간 원자적 트랜잭션, +강제 격리, 장애 시 자동 재시도, 장기 실행 영역 관리자, 네트워크 성능, +macOS 실행을 증명하지 않는다. 부분 성공을 호출자에게 명확히 보여주는 +것이 현재 계약이다. 이 결과를 일반적인 Kitex 수준 RPC 성능 주장으로 +확장하지 않는다. + +[최신 소스 묶음](../../prototypes/connections/results/2026-09-24-mirror-source/pure-source-preview.tar.gz)은 +170개 파일, 압축 332,792 B다. [분리된 디렉터리의 검증](../../prototypes/connections/results/2026-09-24-mirror-source/source-check-linux-aarch64.json)에서 +C·PTY·실제 전달·저장 형식 내보내기·Linux 릴리스 상한을 다시 통과했고, +독립 생성한 압축 파일의 해시도 일치했다. 앞선 묶음과 비교하면 기존 C·헤더는 +동일하며 새 C 파일은 이 선택형 명령 하나다. diff --git a/docs/research/064-release-callback-reentrancy.md b/docs/research/064-release-callback-reentrancy.md new file mode 100644 index 0000000..68ce819 --- /dev/null +++ b/docs/research/064-release-callback-reentrancy.md @@ -0,0 +1,29 @@ +# 064 — 해제 콜백의 영역 재진입 차단 + +실행 코어는 읽기·쓰기 효과 중 같은 영역으로 다시 들어오는 호출을 거절한다. +반면 버퍼의 `release` 콜백은 취소와 티켓 회수 중에도 호출되는데, 그동안 +`running`이 꺼져 있었다. 콜백이 같은 영역의 티켓을 해제하거나 새 작업을 +받아들이면, 바깥쪽 회수 코드가 작업 슬롯과 예산 장부를 계속 수정할 수 있었다. + +[`runtime.c`](../../prototypes/connections/runtime.c)는 자원 해제 콜백 동안 해당 +영역을 실행 중으로 표시하고 이전 상태를 복원한다. 새 상태 필드나 작업별 +할당은 없다. [`test-runtime.c`](../../prototypes/connections/test-runtime.c)는 +취소, 대기 작업 해제, 유효하지 않은 호스트 할당 반환에서 콜백이 같은 티켓을 +해제하거나 새 작업을 접수하려는 시도가 거절되는지 검사한다. + +Linux AArch64의 [기본 코어 검사](../../prototypes/connections/results/2026-09-24-release-callback/core.json)는 +`text` **5,908→5,980 B** (+72 B), data/BSS 0 B, 영역 128 B, 작업 슬롯 224 B를 +기록했다. [선택형 비동기 코어](../../prototypes/connections/results/2026-09-24-release-callback/async-core.json)는 +7,372 B로 자체 8 KiB 상한을 통과했다. [수명 검사](../../prototypes/connections/results/2026-09-24-release-callback/runtime.json), +ASan·UBSan 실행 및 `make check-release`의 실제 문서·HTTP·코드·RSS 검사가 +통과했다. 이 검사는 해제 콜백의 **같은 영역** 재진입을 막는 것이며, +프로세스 내 메모리 격리나 다른 영역으로의 호출 권한을 제공하지 않는다. + +수정한 코드가 담긴 [기본 CLI 묶음](../../prototypes/connections/results/2026-09-24-release-callback/pure-document-micro-linux-aarch64.tar.gz)은 +22,758 B이며 [분리된 위치의 편집·복원 검사](../../prototypes/connections/results/2026-09-24-release-callback/micro-package-check.json)를 +통과했다. [소스 묶음](../../prototypes/connections/results/2026-09-24-release-callback/pure-source-preview.tar.gz)은 +170개 파일, 압축 333,037 B다. [깨끗한 추출·빌드 검사](../../prototypes/connections/results/2026-09-24-release-callback/source-package-check.json)는 +PTY·문서 RSS·프로세스 내 전달·저장 형식 내보내기·Linux 릴리스 상한을 다시 +실행했다. 이 소스 배포 크기는 런타임의 상주 메모리 수치가 아니다. +독립적으로 다시 생성한 소스 묶음도 SHA-256 +`cff657597c0cc33bd9185c8460e0d84f0843da20a786578324ac612fa255c2b4`로 일치했다. diff --git a/docs/research/065-native-save-latency.md b/docs/research/065-native-save-latency.md new file mode 100644 index 0000000..1789ee2 --- /dev/null +++ b/docs/research/065-native-save-latency.md @@ -0,0 +1,57 @@ +# 065 — 실제 네이티브 편집기의 1바이트 저장 완료 시간 + +큰 문서의 작은 수정에서 페이지 저장소가 CLI 실험 밖에서도 유용한지 확인했다. +같은 빌드 시 준비된 `editor.pure`와 네이티브 TUI를 기본 불변 파일 저장소, +선택형 16 KiB 하이브리드 페이지 저장소에 각각 연결했다. 입력·커서·화면은 +같은 코드다. TUI에서 마지막 문자를 바꾸고 Ctrl+S를 보낸 순간부터 +**내구성 저장 완료가 화면에 표시된 프레임**까지 측정했다. 이 시간에는 초안 +비교, Pure 실행, 파일 `fsync`, 화면 갱신이 포함된다. 최초 문서 생성과 +사후의 독립 CLI 읽기는 제외했다. 매 세션의 최종 본문과 리비전은 그 CLI로 확인했다. + +2026-09-24 Linux AArch64, ext4 `/dev/mmcblk0p2`에서 256 B와 1 MiB 문서별로 +각 저장소를 세 세션씩 열고 세션마다 8회 수정했다. 저장소 순서를 세션마다 +뒤집었다. 아래 수치는 저장소·문서 크기별 **24회**의 중앙값이다. +[측정 코드](../../research/experiments/006-native-save-latency/measure.py)와 +[매회 시간·실행물 해시·최종 본문 해시](../../research/experiments/006-native-save-latency/2026-09-24-ext4-linux-aarch64.json)를 +남겼다. + +| Ctrl+S → 저장 완료 표시 | 기본 파일 | 선택형 페이지 | +|---|---:|---:| +| 256 B 문서의 마지막 1 B 수정 | 8.54 ms | 9.23 ms | +| 1 MiB 문서의 마지막 1 B 수정 | 93.17 ms | 22.04 ms | + +1 MiB의 세 세션별 중앙값은 기본 93.19·93.83·92.17 ms, 페이지 +21.34·22.04·22.86 ms였다. 이 작업에서는 약 **4.2배** 차이가 났다. +256 B에는 페이지 경로의 이득이 없다. 최대 지연도 각각 349.60·97.70 ms로 +흔들렸으므로 꼬리 지연의 보편적 상한은 주장하지 않는다. 장치의 캐시·동시 +작업·온도는 통제하지 않았다. + +비용도 따로 기록했다. 같은 Linux 빌드의 고정 정의 TUI 링크 `text`는 +**32,079→37,021 B** (+4,942 B), 소스 정의 TUI는 **48,684→53,642 B** +(+4,958 B)다. 현재의 소스 CLI+TUI 압축 묶음은 +[기본 51,117 B](../../prototypes/connections/results/2026-09-24-native-save/pure-document-flat-cli-tui-linux-aarch64.tar.gz)와 +[선택형 페이지 57,788 B](../../prototypes/connections/results/2026-09-24-native-save/pure-document-hybrid-cli-tui-linux-aarch64.tar.gz)다. +각각 [기본형](../../prototypes/connections/results/2026-09-24-native-save/flat-package-check.json)과 +[페이지형](../../prototypes/connections/results/2026-09-24-native-save/package-check.json)을 +분리된 디렉터리에서 검증했다. +선택형 CLI+TUI 묶음을 다시 생성해도 SHA-256 +`b3c156eb7880ee4cb91b3fe52854cb5069ef035d082b7d539fa2a79592247ec5`로 일치했다. +실제 [소스 TUI 검사](../../prototypes/connections/results/2026-09-24-native-save/hybrid-source-tui.json)는 +1 MiB 편집·UTF-8·충돌·1,024개 문서 조작을 통과했고, 한 실행의 피크 RSS는 +3,200 KiB였다. [기본형의 별도 실행](../../prototypes/connections/results/2026-09-24-native-save/flat-source-tui.json)은 +3,216 KiB였다. 이 한 번씩의 RSS로 미세한 메모리 우열을 판정하지 않는다. + +선택형 패키지는 `.pure`를 실행 때 읽어 정의를 바꿀 수 있다. 작은 문서는 +기존 `PUREB001`이고 큰 문서는 `PUREP001`이 된다. 기본 저장기는 큰 페이지 +문서를 직접 읽을 수 없으므로 별도 저장소에서 사용하고, 기본 형식으로 돌아갈 +때는 [오프라인 내보내기](062-paged-store-rollback.md)를 거쳐야 한다. +16 KiB OS 페이지가 필요한 이 실험은 Linux에서만 실행했고 macOS는 미검증이다. +기본 코어와 기본 TUI는 변경하지 않았다. 이 비용·형식 경계 때문에 페이지 +저장소는 **명시적 선택 기능**으로 유지한다. + +최신 [소스 배포본](../../prototypes/connections/results/2026-09-24-native-save/pure-source-preview.tar.gz)은 +171개 파일, 압축 333,925 B다. [다른 디렉터리의 깨끗한 빌드](../../prototypes/connections/results/2026-09-24-native-save/source-package-check.json)에서 +이 선택형 TUI의 빌드·실제 문서 작업·패키징·이동 후 검사를 기본 C·PTY·전달· +내보내기·Linux 릴리스 검사와 함께 통과했다. 별도 생성한 소스 배포본도 +SHA-256 `b258788cc9f90d8e6eccc964c8c9f4e2c5460df3dfbb9da07268da07799691e4`로 +일치했다. 소스 압축 크기는 상주 메모리와 다르다. diff --git a/docs/research/066-application-boundary.md b/docs/research/066-application-boundary.md new file mode 100644 index 0000000..44e6d86 --- /dev/null +++ b/docs/research/066-application-boundary.md @@ -0,0 +1,65 @@ +# 066 — 작은 코어에서 하나의 앱 경계 만들기 + +2026-09-24. 첫 공개 체크포인트를 위한 당시 코드 경계 조사. 아래 수치는 변경 전 +기준선이고, 문서 해석을 코어 밖으로 옮긴 결과는 [068](068-opaque-app-core.md)에 있다. + +## 현재 코드가 증명하는 것과 못 하는 것 + +`prototypes/connections/runtime.c`는 입력 접수, 바이트 예산, 작업 슬롯, 완료, +대여와 다른 영역으로의 무복사 전달을 소유한다. 하지만 실행 단계에서 +`PureProgram`의 저장 전용 `read`/`write`/`splice`를 직접 해석하고 +`PureHost.read`/`write`/`splice`를 호출한다. 따라서 현재 5,980 B 코어는 +**범용 앱 코어의 크기**가 아니라 **문서 저장 실행 프로필의 크기**다. + +`prototypes/workbench/native/app.c`는 별도의 `PureScalarPlan`을 실행한다. +터치·키·숫자 입력은 `write("event")`, 상태 스냅샷은 `read("snapshot")`, +복원은 `write("restore")`라는 호스트 키로 연결된다. 수명·예산·저장·복원은 +같은 `PureRuntime`을 통과하지만, 작성한 scalar 규칙이 문서의 바이트 효과를 +선택하거나 거절하지는 못한다. 두 문법을 한 앱 표현이라고 주장할 수 없다. + +이 차이를 없애려고 모든 입력을 범용 객체로 포장하거나 코어에 새 VM, +프로세스 관리자, 프로토콜 파서를 추가하면 마이크로 목표와 충돌한다. +기존 작업물에 **숫자 계산과 바이트 효과가 함께 필요한 한 작업**을 추가해 +필요한 경계만 추출하는 순서가 더 검증 가능하다. + +## 첫 통합 작업과 실패 조건 + +기존 문서 편집 앱의 부분 수정 경로에 작성 가능한 크기 정책을 연결한다. +입력은 현재 문서 길이·제거 길이·삽입 길이·설정된 한도이고, 결과는 +저장 효과의 허용 여부와 오류다. 허용되면 기존의 조건부 splice가 실제 +바이트를 처리하고, 거절되면 저장소와 리비전은 변하지 않는다. 정책은 +CLI·TUI·인증 피어가 같은 준비된 앱 정의에서 읽어야 한다. 단순히 각 +호스트에 같은 `if`를 복사하면 이 실험은 실패다. 정책만 있는 새 데모나 +별도 언어를 만드는 것도 실패다. + +필요한 입력·출력 계약은 작다: 검증된 정수 길이, 조건부 효과 결정, +대여한 바이트 범위, 버전, 결과 상태. 호스트가 소유한 파일·소켓·화면 +핸들은 이 계약의 일부가 아니다. 연결될 때만 작업 슬롯과 payload 예산을 +사용하고, 유휴 앱마다 작업자·큐·힙을 만들지 않는다. + +검증은 실제 문서에 대해 한도가 허용/거절하는 수정, 버전 충돌, 1 MiB +본문, CLI/TUI/피어 간 동일 결과, 실패 시 상태·리비전 불변, 대여 회수를 +포함한다. 정책을 수정한 뒤 동작이 바뀌는 작성 경로와, 파서를 제외한 +고정 실행 경로도 각각 확인한다. 비교 대상은 정책을 호스트 C 코드에 +직접 구현한 동등 기능이다. 지연·메모리 우위는 측정 전까지 주장하지 않는다. + +## 코어 경계와 크기 합격 조건 + +현재 저장 전용 `PureProgram` 해석과 `splice` 필드는 앱/효과 어댑터의 +후보이다. 우선 실제 문서 앱과 scalar 앱의 작업에서 공통인 접수·예산· +완료·대여·전달만 분리하고, 각 앱의 계산과 외부 I/O는 선택형 코드로 +유지한다. 이 과정에서 기존의 취소, 비동기 완료, 다중 영역 전달 계약을 +깨지 않는지 확인해야 한다. 이름만 `read/write`에서 `input/output`으로 +바꾸는 작업은 의미가 없다. + +현재 [기본 코어 예산](../../prototypes/connections/core-budget.json)은 Linux +AArch64/GCC의 `text` 6 KiB, 유휴 영역 128 B, 작업 슬롯 216 B 상한이다. +통합 뒤에도 기본 코어, 선택한 앱의 링크 코드·전체 RSS·유휴 인스턴스, +실제 작업당 payload/복사량을 **각각** 비교한다. 코어 밖으로 비용을 +옮겨서 전체 앱이 커졌다면 개선으로 취급하지 않는다. 문서 앱의 기존 +크기·내구성·복원 검사를 통과하지 못해도 채택하지 않는다. macOS arm64 +빌드·실행 증거는 별도로 필요하다. + +이 통합이 성공한 뒤에야 UI와 저장 외의 네트워크/RPC 입력에 같은 계약을 +적용한다. 이것은 OS 프로세스, pod 또는 VM을 상시 생성하겠다는 계획이 +아니다. 강제 격리가 필요한 배치는 호스트 경계의 선택으로 남긴다. diff --git a/docs/research/067-work-slot-layout.md b/docs/research/067-work-slot-layout.md new file mode 100644 index 0000000..55097be --- /dev/null +++ b/docs/research/067-work-slot-layout.md @@ -0,0 +1,46 @@ +# 067 — 작업 슬롯 8 B 절감, 실행 코드 증가 없음 + +2026-09-24. Linux AArch64, GCC 14.2, C17 `-O2`; 첫 공개 체크포인트의 +반복 메모리 경계 수정이다. 현재 `PureWork`는 내부 저장 구조이며 ABI나 +디스크·네트워크 형식이 아니다. + +64 B 이름과 종단 NUL, 수명 티켓, 자원·결과·예산 필드를 그대로 둔 채 +필드 순서를 바꿨다. 같은 64비트 호스트에서 `sizeof(PureWork)`는 +**224→216 B**다. 1,024개 동시 작업 슬롯의 정적 배열이라면 정확히 +8,192 B를 덜 예약한다. 실제 서비스 RSS가 8,192 B 줄었다고 주장하지 +않는다. 유휴 앱은 이미 작업 슬롯을 분리하므로 이득은 활성 슬롯에만 있다. +64비트 C 호스트에는 슬롯 216 B, 기본 영역 128 B, 계측 영역 200 B의 +컴파일 시 검사를 추가했다. 크기 상한 JSON도 216 B로 낮췄다. + +| Linux AArch64 결과 | 이전 | 현재 | +|---|---:|---:| +| 기본 코어 오브젝트 `text` | 5,980 B | 5,980 B | +| 선택형 비동기 코어 오브젝트 `text` | 7,372 B | 7,328 B | +| 기본 영역 / 활성 작업 슬롯 | 128 / 224 B | 128 / 216 B | +| 소스 CLI / HTTP 링크 `text` | 35,863 / 43,978 B | 35,855 / 43,826 B | +| 고정 CLI / HTTP 링크 `text` | 19,324 / 26,589 B | 19,316 / 26,437 B | +| 고정 / 소스 TUI 링크 `text` | 32,079 / 48,684 B | 32,071 / 48,676 B | + +계측 프로필은 이 필드 배치에서 코어 오브젝트 `text` 5,968 B, +영역 200 B다. 기본 코어보다 코드 섹션이 12 B 작게 나온 것은 이 +컴파일러의 코드 생성 결과일 뿐 계측 비용이 없다는 뜻이 아니다. +기본 앱의 실제 1 MiB 편집 피크 RSS는 이번 Linux 검사에서 소스 CLI +2,080 KiB, 고정 CLI 2,112 KiB, 고정/소스 TUI 3,168/3,200 KiB였다. +이전 측정과의 인과적 RSS 차이는 주장하지 않는다. + +`make check-release`, `check-async`, `check-async-server`, `check-mirror`, +`check-tui`, `check-source-tui`, `check-portable`을 통과했다. C 호스트를 +현재 소스와 기존 동일 해시의 Zig scalar 라이브러리로 다시 링크해 +작성 앱의 세 인스턴스 입력·상태·저장·복원·정의 교체도 통과했다. +이 검사는 Zig 라이브러리를 새로 빌드한 증거가 아니다. + +[새 소스 묶음](../../prototypes/connections/results/2026-09-24-work-slot/pure-source-preview.tar.gz)은 +171개 파일, 334,267 B이고, [깨끗한 추출 검사](../../prototypes/connections/results/2026-09-24-work-slot/source-package-check.json)는 +C·실제 TUI·문서 RSS·수정한 정의·영역 간 1 MiB 전달·페이지 형식 +내보내기·선택형 TUI 패키지·Linux 릴리스 경로를 재실행했다. +[소스 CLI](../../prototypes/connections/results/2026-09-24-work-slot/pure-document-source-cli-linux-aarch64.tar.gz), +[고정 CLI](../../prototypes/connections/results/2026-09-24-work-slot/pure-document-fixed-cli-linux-aarch64.tar.gz), +[고정 CLI+TUI](../../prototypes/connections/results/2026-09-24-work-slot/pure-document-fixed-cli-tui-linux-aarch64.tar.gz), +[소스 CLI+TUI](../../prototypes/connections/results/2026-09-24-work-slot/pure-document-source-cli-tui-linux-aarch64.tar.gz)는 +각각 이동 후 실제 문서 작업을 통과했다. 압축 크기는 차례로 22,784, +12,972, 32,119, 51,167 B다. macOS arm64 실행은 여전히 미검증이다. diff --git a/docs/research/068-opaque-app-core.md b/docs/research/068-opaque-app-core.md new file mode 100644 index 0000000..419338f --- /dev/null +++ b/docs/research/068-opaque-app-core.md @@ -0,0 +1,74 @@ +# 068 — 문서 해석을 떼어낸 마이크로 실행 코어 + +2026-09-24. Linux AArch64, GCC 14.2, C17 `-O2`. [066](066-application-boundary.md)의 +첫 경계 수정이다. 목적은 새 운영체제 기능을 늘리는 것이 아니라, 모든 앱이 +링크해야 하는 실행 코어에서 문서 전용 연산을 제거하는 것이다. + +`PureRuntime`은 이제 앱 정의를 불투명 포인터로 보관한다. 코어는 입력 접수, +작업 슬롯·바이트 예산, 완료, 결과 대여, 취소, 영역 간 전달을 관리한다. +문서의 필드 선택·응답 구간·splice 계산은 선택형 `store-port.c`가 해석한다. +코어 아카이브는 `runtime.o` 한 개이며 `evaluate.o`나 `PureProgram`을 +포함하지 않는다. scalar 앱은 정의 포인터 없이 같은 코어를 쓴다. +비동기 효과는 원시 입력을 빌려 주고, 선택한 호스트가 문서 규칙을 적용한 +뒤 입력 내부의 응답 구간을 돌려준다. 코어는 이 대여 범위를 검사한다. + +| 비용 | 이전 067 | 이번 결과 | 차이 | +|---|---:|---:|---:| +| 기본 코어 오브젝트 `text` | 5,980 B | **4,632 B** | -1,348 B | +| 비동기 코어 오브젝트 `text` | 7,328 B | **5,724 B** | -1,604 B | +| 기본 영역 / 작업 슬롯 | 128 / 216 B | **120 / 216 B** | -8 / 0 B | +| 계측 영역 | 200 B | **192 B** | -8 B | +| 소스 CLI / HTTP 링크 `text` | 35,855 / 43,826 B | **35,743 / 43,950 B** | -112 / +124 B | +| 고정 CLI / HTTP 링크 `text` | 19,316 / 26,437 B | **19,204 / 26,625 B** | -112 / +188 B | +| 고정 / 소스 TUI 링크 `text` | 32,071 / 48,676 B | **31,959 / 48,572 B** | -112 / -104 B | +| 비동기 소스 HTTP 링크 `text` | 48,537 B | **48,241 B** | -296 B | + +기본 코어의 `text`는 약 22.5% 줄었지만, **코어 절감량을 전체 앱 절감량으로 +말할 수 없다.** 문서 해석은 선택한 호스트에 필요하고, 특히 고정 HTTP 실행물은 +188 B 커졌다. 그래서 해당 호스트의 Linux/GCC 회귀 상한을 26,624→26,880 B로 +검토해 조정했다. 코어 상한은 6,144→4,864 B, 비동기 코어 상한은 +7,680→6,144 B로 낮췄다. 두 코어의 `data`/`bss`는 각각 0 B다. +이 수치는 오브젝트·링크 섹션이고 libc, 스택, 파일 매핑, payload와 +전체 프로세스 메모리를 포함하지 않는다. + +실제 1 MiB 문서 검사에서는 소스·고정 CLI 편집 피크가 모두 2,128 KiB, +고정/소스 TUI는 3,168/3,200 KiB였다. 고정 HTTP 호스트의 테스트 중 피크는 +2,112 KiB였다. 이전 런과의 RSS 인과 차이는 주장하지 않는다. 입력의 +할당·복사 계약도 이번 변경으로 자동 최적화된 것은 아니다. + +`check-release`, `check-async`, `check-async-server`, `check-fixed-server`, +`check-mirror`, `check-tui`, `check-source-tui`, `check-portable`을 통과했다. +동기 고정·소스 HTTP와 선택형 비동기 소스 HTTP는 각각 1,024개 실제 +8 KiB 문서, 8개 동시 클라이언트, 4,096개 작업에서 같은 최종 문서 해시를 +만들었다. 각 호스트는 종료 시 활성 작업·보존 payload가 0이었고, +측정 피크 RSS는 고정 1,680 KiB, 소스 1,712 KiB, 비동기 소스 1,760 KiB였다. +이는 기능·자원 회귀 검사이며 처리량 우위의 증거는 아니다. +선택형 TLS 피어와 서버 전용 피어도 인증·외부 클라이언트·1 MiB 문서· +재시작 검사를 통과했다. 링크 `text`는 각각 46,894 / 43,422 B이고, +OpenSSL 공유 라이브러리의 코드·메모리는 이 섹션 크기에 포함되지 않는다. +ASan/UBSan을 적용한 C 계약 검사도 통과했다. 이 환경의 ptrace 아래에서는 +LeakSanitizer가 실행되지 않아 해당 검사에서는 누수 탐지를 끄고 실행했다. +기존 해시의 Zig scalar 정적 라이브러리를 현재 C 호스트와 다시 링크한 +세 인스턴스 입력·저장·복원 검사와 고정 정의 두 인스턴스 검사도 통과했다. +Zig 자체를 새로 빌드한 +증거는 아니다. + +[새 소스 묶음](../../prototypes/connections/results/2026-09-24-opaque-core/pure-source-preview.tar.gz)은 +171개 파일, 334,682 B다. [깨끗한 추출 검사](../../prototypes/connections/results/2026-09-24-opaque-core/source-package-check.json)는 +C·TUI·문서 RSS·수정한 `.pure` 동작·1 MiB 영역 간 전달·선택형 저장 형식 +내보내기·Linux 릴리스 경로를 다시 실행했다. +[소스 CLI](../../prototypes/connections/results/2026-09-24-opaque-core/pure-document-source-cli-linux-aarch64.tar.gz), +[고정 CLI](../../prototypes/connections/results/2026-09-24-opaque-core/pure-document-fixed-cli-linux-aarch64.tar.gz), +[고정 CLI+TUI](../../prototypes/connections/results/2026-09-24-opaque-core/pure-document-fixed-cli-tui-linux-aarch64.tar.gz), +[소스 CLI+TUI](../../prototypes/connections/results/2026-09-24-opaque-core/pure-document-source-cli-tui-linux-aarch64.tar.gz)는 +각각 이동 후 실제 문서 작업을 통과했다. 압축 크기는 순서대로 +22,655 / 12,886 / 31,965 / 51,005 B다. 압축 크기는 RSS가 아니다. + +이 변경으로 하나의 작성 가능한 앱 모델이 생긴 것은 아니다. 코어에는 여전히 +문서에서 물려받은 64 B ASCII 키와 `READ`/`WRITE` 두 입력 종류가 있다. +scalar 규칙과 문서 효과는 서로 다른 작성 모델이며, 네트워크/RPC와 UI의 +일반 연결도 아직 합쳐지지 않았다. 다음 검증은 실제 앱의 숫자 정책이 문서 +수정을 허용·거절하는 한 경로를 동일한 작성 정의에서 실행하는 것이다. +그때도 문서·네트워크·UI 코드를 기본 코어에 넣지 않고, 선택한 앱의 +전체 링크 비용·프로세스 RSS·작업별 보존 바이트를 함께 비교해야 한다. +macOS arm64 빌드와 실행은 아직 수행하지 않았다. diff --git a/docs/research/069-atomic-authored-size-policy.md b/docs/research/069-atomic-authored-size-policy.md new file mode 100644 index 0000000..4718e89 --- /dev/null +++ b/docs/research/069-atomic-authored-size-policy.md @@ -0,0 +1,72 @@ +# 069 — 작성한 크기 정책을 실제 문서 효과에 연결 + +2026-09-24. Linux AArch64, GCC 14.2, C17 `-O2`. 첫 공개 체크포인트를 향한 +실제 앱 경계 검증이다. [068](068-opaque-app-core.md)의 불투명 실행 코어는 +변경하지 않았다. + +[`bounded-editor.pure`](../../prototypes/connections/examples/bounded-editor.pure)는 +`store.splice(..., edit.insert, 8)`로 수정 후 문서의 최대 바이트 수를 작성한다. +준비 단계에서 상수를 검사하고, 저장 어댑터가 그 값을 효과에 전달한다. +두 저장 구현은 쓰기 잠금 안에서 현재 버전의 크기, 제거 길이, 삽입 길이로 +최종 크기를 계산한다. 초과하면 새 파일·리비전을 공개하지 않는다. +정책 검사를 위한 별도 읽기나 전체 문서 버퍼는 만들지 않는다. 요청 본문이 +상한 값을 결정하지도 못한다. 예상 리비전 충돌을 먼저 판단하므로 낡은 +요청은 정책 결과로 충돌을 가리지 않는다. + +CLI에서 6 B 생성→2 B 추가는 허용됐고 1 B 추가는 거절됐으며, 기존 8 B +내용과 리비전 2가 유지됐다. 1 B 제자리 교체는 허용됐다. 정의의 상한을 +4 B로 바꾸고 새 소스 호스트를 시작하자 같은 길이의 교체도 거절되고, +4 B 삭제만 허용됐다. 고정 빌드는 처음 준비한 8 B 정책을 유지했다. +소스·고정 HTTP는 같은 저장 파일에서 동일한 결과를 냈고, 두 TUI는 +저장 거절 뒤 초안을 보존했다. 선택형 상호 인증 TLS 피어도 초과 수정은 +상태 5(`limit`)로 거절하고 허용한 삭제는 CLI에서 같은 버전으로 읽혔다. +[실행 증거](../../prototypes/connections/results/2026-09-24-atomic-policy/policy.json)와 +[인증 피어 증거](../../prototypes/connections/results/2026-09-24-atomic-policy/policy-peer.json)에 +재현된 시나리오를 기록했다. + +| Linux AArch64 `text` / 저장 비용 | 이전 068 | 현재 | 변화 | +|---|---:|---:|---:| +| 기본 실행 코어 | 4,632 B | **4,632 B** | 0 | +| 유휴 영역 / 활성 슬롯 | 120 / 216 B | **120 / 216 B** | 0 / 0 | +| 준비된 문서 정의 | 680 B | **680 B** | 0 | +| 소스 CLI / HTTP | 35,743 / 43,950 B | **36,135 / 44,342 B** | +392 / +392 B | +| 고정 CLI / HTTP | 19,204 / 26,625 B | **19,268 / 26,689 B** | +64 / +64 B | +| 고정 / 소스 TUI | 31,959 / 48,572 B | **32,023 / 48,972 B** | +64 / +400 B | +| 선택형 비동기 소스 HTTP | 48,241 B | **48,633 B** | +392 B | +| 선택형 TLS 겸용 피어 | 46,894 B | **47,286 B** | +392 B | +| 선택형 혼합 저장소 HTTP | 49,133 B | **49,589 B** | +456 B | + +`PureProgram`의 정책 값은 기존 정렬 패딩에 배치했다. 64비트 호스트의 +680 B 크기를 컴파일 시 검사한다. 기본 코어 아카이브에는 계속 `runtime.o` +하나만 있으며, 문서 평가기·정책·저장소는 포함되지 않는다. 선택형 혼합 +저장소 HTTP는 기존 49,152 B 상한을 437 B 초과했다. 이전 소스 묶음을 +같은 도구로 다시 빌드해 49,133 B 기준을 확인했고, 456 B 증가를 검토해 +선택형 상한만 50,176 B로 조정했다. 기본 코어 상한은 그대로 4,864 B다. +소스 TUI는 현재 49,152 B 상한까지 180 B만 남는다. 이후 기능은 이 +선택형 실행물의 코드 구조를 다시 검토해야 하며 상한을 자동으로 늘리지 않는다. +이 수치들은 코드 섹션이고 libc/OpenSSL, 스택, payload 및 저장 파일을 +포함하지 않는다. 기존 1 MiB 문서 검사의 피크 RSS는 소스 CLI 2,080 KiB, +고정 CLI 2,112 KiB였다. 런 간 차이를 정책의 RAM 효과라고 주장하지 않는다. + +`check-release`에는 실제 CLI·HTTP·TUI 정책 경로를 넣었다. +`check-policy-peer`, `check-hybrid`, `check-async`, `check-async-server`, +기존 TUI·인증 피어 검사와 ASan/UBSan C·정책 검사가 통과했다. 이 환경의 +ptrace 아래에서 LeakSanitizer는 실행되지 않아 누수 탐지는 끄고 실행했다. +현재 C 호스트를 기존 Zig scalar 정적 라이브러리와 다시 링크한 세 앱 +인스턴스 검사도 통과했지만 Zig를 새로 빌드한 증거는 아니다. + +[소스 묶음](../../prototypes/connections/results/2026-09-24-atomic-policy/pure-source-preview.tar.gz)은 +173개 파일이다. [깨끗한 추출 검사](../../prototypes/connections/results/2026-09-24-atomic-policy/source-package-check.json)는 +릴리스·TUI·TLS·문서 전달·저장 형식 변환 경로를 다시 실행했다. +[소스 CLI](../../prototypes/connections/results/2026-09-24-atomic-policy/pure-document-source-cli-linux-aarch64.tar.gz), +[고정 CLI](../../prototypes/connections/results/2026-09-24-atomic-policy/pure-document-fixed-cli-linux-aarch64.tar.gz), +[고정 CLI+TUI](../../prototypes/connections/results/2026-09-24-atomic-policy/pure-document-fixed-cli-tui-linux-aarch64.tar.gz), +[소스 CLI+TUI](../../prototypes/connections/results/2026-09-24-atomic-policy/pure-document-source-cli-tui-linux-aarch64.tar.gz)는 +각각 이동한 위치에서 실제 문서 작업을 통과했다. 압축 크기는 순서대로 +22,862 / 12,966 / 32,144 / 51,398 B다. + +이것은 **문서 수정 효과 안의 한 숫자 정책**이다. scalar 앱의 규칙 언어와 +문서 앱이 하나로 합쳐진 것은 아니며, 일반 산술·조건부 효과·여러 효과, +네트워크/RPC의 범용 연결도 아직 없다. 바이트 상한은 문자나 문자소 +수의 상한이 아니다. macOS arm64 CI에는 정책 검사를 추가했지만, 해당 +빌드·실행 결과는 아직 확인하지 못했다. diff --git a/docs/research/070-micro-default-build.md b/docs/research/070-micro-default-build.md new file mode 100644 index 0000000..33b8408 --- /dev/null +++ b/docs/research/070-micro-default-build.md @@ -0,0 +1,31 @@ +# 070 — 기본 빌드의 비용 경계 + +2026-09-24. Linux AArch64, GCC 14.2, C17 `-O2`. 마이크로 런타임의 기본 빌드가 +선택형 기능을 실수로 끌어오지 않도록 `make all`을 코어와 고정 정의 CLI로 제한했다. +`editor.pure`를 빌드할 때 준비하는 작은 컴파일러 실행물도 생성되지만, 완성된 +CLI에는 링크하거나 배포하지 않는다. 실행 중 `.pure`를 읽고 바꾸는 CLI는 +`make editor`, HTTP는 `make server`, TUI와 인증 피어도 각각 명시적 선택이다. + +깨끗한 소스에서 기본 `make`를 실행했을 때 빌드 디렉터리에는 +`runtime.o`, `libpure-execution.a`, `pure-compile`, `embedded-plan.h`, +`pure-edit-fixed`만 있었다. 새 [소스 묶음](../../prototypes/connections/results/2026-09-24-micro-default/pure-source-preview.tar.gz)을 +다른 디렉터리에 풀어 같은 목록을 검사하고, CLI·TUI의 실제 PTY 작업, +1 MiB 문서 RSS, 소스 정의 수정, Linux 릴리스 검사를 다시 실행했다. +[추출 검사 결과](../../prototypes/connections/results/2026-09-24-micro-default/source-package-check.json)에 +재현 경로를 기록했다. + +| 비용 | 기본 고정 실행 경로 | 소스 읽기 선택 경로 | +|---|---:|---:| +| 코어 `text` | 4,632 B | 동일 | +| 문서 CLI 링크 `text` | 19,268 B | 36,135 B | +| 1 MiB 문서 편집 피크 RSS | 2,112 KiB | 2,080 KiB | + +이 변경은 링크 코드와 기본 의존성 선택을 줄였으며, RSS 개선을 입증한 것은 아니다. +측정된 RSS는 오히려 고정 CLI 쪽이 32 KiB 높았고 실행·페이지 배치의 변동과 +분리할 근거가 없다. 코어 `text`와 120 B 유휴 영역, 216 B 작업 슬롯은 +변하지 않았다. 두 CLI의 직접 동적 라이브러리 의존성은 이 호스트에서 libc 하나다. +빌드용 컴파일러, libc, 스택과 저장 파일 비용을 코어 수치에 포함하지 않았다. + +이후 앱 기능은 기본 코어에 넣기 전에 필수 의미론인지 확인하고, 선택한 +실행물의 링크 코드·실제 전체 메모리·배포 묶음 비용을 별도로 검사한다. +선택형 어댑터의 기능을 기본 빌드에 추가할 때는 같은 검사를 다시 통과해야 한다. |
