Files
core2026/docs/architecture/npc-unification-timing-benchmark.md
T
Hide_D 20c04283fb perf(game-engine): 대규모 NPC 천통 시간을 계측
시나리오 2601의 880명·94도시를 DB와 Redis 없이 자동 실행하고 커맨드, 수뇌 여부, 연월별 시간을 JSON으로 기록한다.
2026-08-15 19:36:38 +00:00

4.4 KiB

NPC 대규모 인메모리 천통 시간 벤치마크

목적

scenario_2601의 장수 roster와 che 지도를 사용해 PostgreSQL·Redis를 연결하지 않은 계산 전용 월드에서 NPC가 건국부터 통일까지 자동 진행하는 시간을 측정한다. 전체 wall time과 함께 다음 구간을 분리한다.

  • 일반/국가 커맨드 실행 시간
  • NPC AI 판단 시간
  • 수뇌(officerLevel >= 5)와 일반 NPC 장수 턴 시간
  • 게임 연월별 월 처리 시간과 국가·장수 수

이 벤치마크는 DB throughput이나 production daemon capacity를 측정하지 않는다. 서버 계산량의 상한/회귀를 재현하는 프로파일이며 실제 운영 wall time에는 PostgreSQL transaction, flush, lease, Redis, API와 다른 프로세스 경합을 별도로 더해야 한다.

실행

기본 실행은 고정 seed, 시나리오 2601, 880명, 94도시, 10분 턴과 300년 안전 상한을 사용한다.

NPC_UNIFICATION_BENCHMARK_CONVERGENCE_ASSIST=none \
NPC_UNIFICATION_BENCHMARK_MAX_YEAR=300 \
NPC_UNIFICATION_BENCHMARK_REPORT_PATH=/dev/shm/npc-unification.json \
pnpm --filter @sammo-ts/game-engine profile:npc-unification-timing

NPC_UNIFICATION_BENCHMARK_CONVERGENCE_ASSIST=none이 무보정 자연 진행이다. 비교용 nation-1-max-city는 매월 1번 국가가 이미 소유한 도시의 인구·내정·방어·성벽과 민심을 최대값으로 복원한다. 국가 선택, 외교, 출병, 전투, 점령, 멸망과 통일 판정을 직접 만들지는 않는다. 대형 fixture가 유한 시간에 통일하도록 한 기존 npcNationUprisingUnification.test.ts의 수렴 보조를 94도시로 확장한 것이다.

실행 경계

포함하는 경로는 다음과 같다.

  • scenario_2601.json 합성 loader, map_che.json, unitset_che.json
  • buildScenarioBootstrap()의 deterministic 초기 배치
  • InMemoryTurnWorld, InMemoryReservedTurnStore, InMemoryTurnProcessor
  • GeneralAI.chooseNationTurn()chooseGeneralTurn()
  • 실제 일반/국가 action definition, 전투, 점령과 국가 멸망
  • test harness의 국가 예약턴 월 갱신, 수입, NPC 세금, 전선 갱신
  • 실제 unificationHandler.ts의 단일 국가·전 도시 소유 판정과 in-memory draft

제외하는 경로는 다음과 같다.

  • PostgreSQL loader, transaction, lease, flush와 통일 최종 archive
  • Redis tournament/realtime
  • scenario event action과 yearbook persistence
  • API, scheduler sleep, worker IPC와 browser

월마다 world.consumeDirtyState()와 예약 큐 acknowledgement를 호출해 log, message와 persistence draft를 메모리 sink로 비운다. DB write는 수행하지 않지만 운영 flush 뒤 메모리 draft가 정리되는 경계는 재현한다.

계측 정의

createReservedTurnHandler()의 선택적 onActionProfiled hook은 hook을 넘긴 경우에만 process.hrtime.bigint()로 다음 시간을 잰다.

  • aiDecisionDurationNs: GeneralAI 생성/재사용과 해당 chooseNationTurn() 또는 chooseGeneralTurn() 호출
  • actionDurationNs: 선택된 action의 parse, constraint, execute와 결과 patch 생성

InMemoryTurnProcessor의 before/after hook은 한 장수 턴 전체를 잰다. 수뇌는 한 턴에 국가 AI와 일반 AI를 모두 실행할 수 있으므로 수뇌의 “판단/턴”은 두 decision 합계를 수뇌 턴 수로 나누어 해석한다. JSON은 커맨드별 count/total/ average/p50/p95/p99/max, 수뇌 여부, 연월별 합계와 process memory high-water를 포함한다.

계측 clock 호출과 배열 수집 자체의 overhead가 wall time에 포함된다. 같은 고정 seed의 독립 실행을 반복하여 결과 state가 같고 wall time만 변하는지 함께 확인해야 한다.

2026-08-15 기준 결과

AMD Ryzen 7 9800X3D, 16 logical CPU, Node v24.18.0, Linux x64의 shared 개발 호스트에서 무보정 자연 실행 두 번 모두 242년 4월, 747개월, 46개국 건국, 총 699,892 장수 턴과 최종 장수 970명으로 동일하게 통일했다. wall time은 483.68초와 466.91초(평균 475.29초)였다. 상세 수치와 커맨드 33종 표는 상위 작업공간의 report/2026-08-15-NPC-대규모-인메모리-천통-벤치마크.md에 기록한다.

수렴 보조 실행 두 번은 225년 11월에 375.24/422.68초로 끝났다. 도시 자원 복원이 전쟁과 통일 연월에 영향을 줬으므로 이 수치는 자연 실행과 분리한 비교 근거로만 사용한다.