# 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인 "변경 특성에 적응하는 정책이 더 유리하다"는 이번 실험으로 채택하거나 기각할 수 없다. 이번에는 고정 간격만 비교했다.