T62+T63+T64 — crownyc VM 프로파일링 묶음 (T29 후속 원인 규명)
개요
집사회 티켓 T62(대구간 완주)+T63(900 변곡점 원인)+T64(VM 프리미티브 단독 벤치)를 격리 환경(/Users/ef/crowny-deacon-scale-test, 원본 crowny-deacon 무접촉)에서 실행.
선행 문서: 2026-07-29-T29-*(O(R) 주장 기각, 900 근방 원인불명 정체 발견).T64 — 분리()/꺼내()/맵넣어() 단독 루프 실측
/tmp/bm_*.한선(맵넣어 N회), /tmp/bs_*.한선(분리+꺼내 N회)로 격리 벤치.
| 연산 | N | 시간(s) | 배율 |
|---|---|---|---|
| 분리+꺼내 | 2000→16000(8x) | 0.086→0.609 | 7.1x — 선형(O(n)) |
| 맵넣어 | 300→3600(12x) | 0.008→0.077 | 9.6x — 근사 선형 |
| 맵넣어 | 5000→16000(3.2x) | 0.151→1.492 | 9.9x — O(n²) |
분리()/꺼내()는 소규모·대규모 모두 깨끗한 선형(문자열.한선 v1.1
수리분이 반영된 상태로 정상). 맵넣어()가 진범 — N이 커질수록(특히
5000 이상) 명확히 O(n²)로 전환된다. T29에서 관찰된 초선형 증가의 실체는
맵 연산이지 문자열 분리/부분이 아니다.T63 — 900 변곡점 원인 규명
crownyc.c 소스 확인 결과:
#define MAP_SLOTS 729(해시맵 블록당 슬롯 수, crownyc.c:3555)case 413: HASH_SET— 키가 없고 현재 블록 슬롯이 가득 차면(729개 전부 사용)
map_block_new()로 새 블록을 체인에 연결(grow)하고, 그 다음부터 모든
삽입(SET)/조회(GET)는 체인 헤드부터 새 키를 못 찾을 때까지 블록을 순차
순회(crownyc.c:7148-7212, while(hbase>0...) for(hp<MAP_SLOTS)...).
- 즉 엔트리 수 K가 729를 넘어 블록이 2개 이상 체인되면, 매 삽입마다 O(체인길이)
결론: T29·T63이 관찰한 "900 근방 변곡점"은 배열 1023/4095 상한이 아니라
해시맵 MAP_SLOTS=729 체인 그로스 임계치다. 729는 900과 근접(±19%)하며,
장부 데이터 특성상 churn(갱신) 재계산 과정에서 맵이 여러 번 재생성/재적재되므로
체감 변곡점이 729보다 약간 늦은 800~900대에서 나타나는 것으로 설명된다.
"배열 1023 캡" 가설(T29에서 제기)은 기각 — 진짜 원인은 해시맵 구조다.
T62 — R5000~10000 대구간 완주 측정 (10분 타임아웃)
기존 driver.한선(가져오기 "미션장부.한선")을 한선씨/ 디렉토리 내부로 옮겨
컴파일(가져오기는 엔트리 파일 디렉토리 기준 — MEMORY 함정 재확인), data/티켓.psv를
R별 합성 파일로 교체하며 대기티켓찾기+티켓목록(_PSV파일유니크최신) 실행.
| R | 결과 | 시간 | 유니크수 |
|---|---|---|---|
| 5000 | 완주 | 32s | 860 |
| 7000 | 비정상 종료(rc=143) | 8s | (출력 없음, "로드: 6017 cubes" 이후 무응답) |
| 10000 | 완주 | 32s | 860 |
중요 발견 2 — 유니크수 860 고정 캡(신규): gen_ledger.pl 규칙상 유니크ID수는
R/3이므로 R=5000→기대 1666, R=10000→기대 3333이어야 하는데, 실측은 R=5000·
R=10000 모두 정확히 860으로 동일하다. 이는 T29의 R=3000 결과(유니크 860)와도
정확히 일치한다. 즉 R이 3000을 넘는 순간부터 유니크수 집계가 860에서 완전히
멈춘다 — 시간뿐 아니라 결과값(유니크수) 자체가 사일런트 캡에 걸려 있다.
heap_ptr/mem_count 충돌 시 [MAP] 힙 부족! 경고가 나야 하는데 stderr에
해당 로그가 없었다(MEM_MAX=144M cubes로 여유 충분 — 힙 고갈은 원인이 아님).
따라서 캡의 정확한 위치(맵 체인 깊이 제한, 혹은 _PSV파일유니크최신 로직
자체의 스캔 한도)는 미규명 — 후속 티켓 필요.
중요 발견 3 — R=7000 이상 원인불명 조기종료: R=7000만 8초만에 rc=143 (SIGTERM 계열)으로 조기 종료, 출력 없이 죽었다. R=5000·10000은 정상 완주했는데 중간값인 7000만 실패한 것은 재현성 의심 — 시스템 리소스 경합(동시 실행 중인 다른 세션·크론) 가능성을 배제 못 함. 재시도 검증 필요.
종합 판정
- 문자열 분리()/꺼내()는 결백 — 선형, VM 레벨 정상.
- 진범 = 맵넣어()의 O(n²) 체인 스캔, 근본 원인 =
crownyc.cHASH_SET/GET의
MAP_SLOTS=729 블록 그로스 후 전체 체인 선형 재스캔 구조.
- 900 변곡점 = 729(MAP_SLOTS) 근접치 — 배열 1023/4095 상한 가설은 기각.
- 신규 발견(미규명): R>3000에서 유니크수 집계가 860에 사일런트 캡 — 시간
관련 파일
- 벤치 스크립트:
/tmp/bm_*.한선(맵넣어),/tmp/bs_*.한선(분리/꺼내) — 임시,
- VM 소스 근거:
/Users/ef/CrownyOS/crownyc/crownyc.cline 3555(MAP_SLOTS),
- 격리 사본:
/Users/ef/crowny-deacon-scale-test/(driver.한선을 한선씨/ 내부로 복사한
crowny-deacon은 무접촉 유지)잔여 이슈 (후속 티켓 제안)
- 유니크수 860 사일런트 캡의 정확한 코드 위치 규명 (최우선 — 데이터 정합성
- R=7000 조기종료(rc=143) 재현 검증 — 우발 사고인지 재현 가능한지
- HASH_SET/GET을 체인 순회 대신 버킷 배열 O(1) 방식으로 재설계하는 근본 수리
crownyc.c MAP_SLOTS 구조 자체 개편) — 대규모 장부 폴링 시나리오에 필수