← 목록
기타 2026-07-11 9KB 읽기 9분

CrownyAI 전용주소(crowny://app) 접속 성능 분석 + 개선

개요

사장님 실측 통증: crowny://app/* 전용탭 접속이 범용(WKWebView)탭보다 훨씬 느리다. 가설(콜드 컴파일 수 초, 웜 90~290ms)을 실측으로 검증하고, 병목을 정정한 뒤 개선했다.

핵심 판정 (실측)

가설과 다른 결과: "콜드 첫 접속 = 수 초"는 개별 앱 단위로는 사실이 아니다.

  • 앱 1개 콜드 오픈(자기 소스만 컴파일): ~140~150ms (계산기 144ms, 터미널 142ms)
  • 앱 1개 웜 오픈(toau 캐시됨): ~90~110ms
  • 진짜 "수 초"가 걸리는 경우는 런처+9앱+web+3D 12개 파이프라인을 처음부터 순서대로 전부 열 때뿐 (실측 3.27s) — 사장님이 세션 첫 실행에 여러 앱을 옮겨다니며 매번 자기 컴파일 비용(~40~100ms)을 반복 체감한 것으로 추정.
진짜 병목 구조: 범용탭(WKWebView)은 최초 로드 후 DOM 증분 갱신이 사실상 0ms인 반면, 전용탭은 키 입력 1회·클릭 1회마다 전체 렌더 파이프라인을 처음부터 다시 돈다(cmd IR VM 스폰 + python 페이지배선 + 렌더 VM 스폰, 매번 ~90~150ms). 이 구조적 반복비용이 체감 "느림"의 본체다.

파이프라인 브레이크다운 (계산기/터미널 기준, 웜)

단계시간비고
cmd IR 생성 (crownyc VM 스폰)~17~19msVM 고정기동 9~13ms 포함
페이지배선.py (python 실행)~43~46mspython 시작 ~22ms + 글리프/RLE 연산 ~22ms
렌더 VM 실행 (페이지렌더러.toau)~45~47msVM 고정기동 9~13ms + 실제 렌더 연산 ~33~37ms
bash 스크립트 오버헤드(env/자가치유 체크)~5~15ms
합계 (웜)~90~110ms앱마다 거의 동일
+ 자기 소스 컴파일(콜드 최초 1회만)+~40~50ms앱별 5개 이하 .한선 파일, hanseonc_high 1개당 5~13ms

개선 (효과 큰 순)

1. 기동 프리워밍 (가장 큰 체감 개선)

native/프리워밍.sh 신규 — src/앱목록.psv 등록 10개 앱 + web + 3D를 순차 1회 호출해 /tmp/*.toau 자가치유 컴파일 + 첫 PPM/폰트 글리프 캐시를 미리 만든다. 단발 실행(데몬 아님), mkdir 원자성 락으로 중복 실행 가드.

crowny-browser.m applicationDidFinishLaunching: 끝부분(UI 생성 직후, _uiReady = YES 다음)에 배선:

objcstatic dispatch_once_t 프리워밍once;
dispatch_once(&프리워밍once, ^{
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
        appRunTask(@"/Users/ef/CrownyBrowser/native/프리워밍.sh", @[], nil);
    });
});
dispatch_once + 스크립트 자체 mkdir 락으로 이중 가드(데몬 아님, 앱 프로세스 수명 내 1회).

효과: 사용자가 실제로 앱을 열 때는 이미 웜 — 콜드 컴파일 지연이 사용자 조작 밖(백그라운드 저우선순위)으로 이동. 전체 프리워밍은 완전 콜드 기준 ~2.1~3.3s 걸리지만 앱 시작 직후 눈에 안 보이는 곳에서 진행.

2. 렌더 자가치유 통합 + stale-toau 함정 수정

native/공통자가치유.sh 신규 — 9개 렌더 스크립트(설정·파일·북마크·날씨·시계·할일·뷰어(메모)·런처·계산기)가 각자 중복 구현하던 자가치유 for-루프를 자가치유() 함수 하나로 통합.

부수 발견/수정: 기존 9개 중 7개(설정·파일·북마크·날씨·시계·할일·런처)는 .toau 재컴파일 판단을 소스 mtime만 비교([ ! -f bin ] || [ src -nt bin ]) — 터미널렌더.sh만 컴파일러/VM mtime까지 비교하는 4항 가드를 쓰고 있었다. 이는 CLAUDE.md에 문서화된 "stale-toau 함정"(hanseonc_high/crownyc 재빌드 시 구버전 .toau가 새 VM과 옵코드 스큐로 조용히 오동작)에 그대로 노출된 상태였다. 통합 함수가 전 스크립트에 4항 가드를 일괄 적용해 이 잠재 버그를 함께 정정했다.

계산기·터미널·뷰어(메모)는 서비스별 부가 로직(계산기상태 등 추가 파일, 메모상세.한선 의존성 무효화)을 유지한 채 공통 함수만 호출하도록 변경.

3. python 시작비용 절감 — 측정 결과 이득 없음 (미적용, 보고만)

  • python3 -c "pass" 단독: 22ms → python3 -S -c "pass": 16ms (27% 개선)
  • 그러나 실제 페이지배선.py 전체 실행에 적용하면: 44ms → 42ms (~1.5ms, 노이즈 수준). 이유: 무거운 부분은 site-packages 스캔이 아니라 subprocess/zipfile/unicodedata 등 이미 컴파일된 표준라이브러리 로딩과 실제 글리프/RLE 연산.
  • .pyc 사전 컴파일(compileall)도 무효 — 스크립트를 python3 script.py로 직접 실행하면 진입 모듈 자체는 바이트코드 캐시 대상이 아님(임포트되는 모듈만 캐시됨).
  • 결론: 지시대로 미적용. 이득이 마진(전체 파이프라인의 1% 미만)이라 리스크(PYTHONPATH 오정렬 시 PIL 임포트 실패 등) 대비 가치 없음.

4. NSTask → 직접 exec (1홉 제거, 안전 확인 후 적용)

renderAndReloadappRunTask(@"/bin/bash", @[script], renv)appRunTask(script, @[], renv)로 변경. 렌더 스크립트 전부 유효 shebang(#!/bin/bash) + 실행권한(+x) 보유 확인(10개 전수 확인). NSTask는 macOS에서 이미 내부적으로 posix_spawn 기반이라 측정상 이득은 노이즈 수준(/bin/bash script.sh vs ./script.sh 직접실행 5회 평균 차이 <5ms)이었지만, 안전하고(실측 검증 완료) 불필요한 홉을 제거하는 구조적 단순화라 적용.

검증

  • 전 12개 렌더 파이프라인(설정/파일/북마크/날씨/시계/할일/메모/런처/계산기/터미널/web/3D) 콜드+웜 각 1회 이상 스모크 — 전부 rc=0, OK / 3D 렌더 완료 확인, 실패 0건.
  • 액션 파이프라인 회귀: 계산기액션.sh 숫자:5, 터미널액션.sh 실행 — 정상 동작(내부에서 수정된 렌더sh 호출).
  • 크라우니브라우저 실제 실행 E2E: CROWNY_AUTOAPP=계산기/터미널 CROWNY_AUTOSHOT=... 자동 검증 하네스로 앱 기동 → 프리워밍 백그라운드 실행 확인(로그) → 앱뷰 PPM 렌더 → 스크린샷 성공, rc=0, 크래시 없음.
  • 빌드 회귀: make(web 타깃) 정상 완료, 경고 2건은 기존 미사용 함수(내 변경과 무관, pre-existing). 렌더브라우저.한선 자가검증의 "검증5 실패"는 완전히 별개 모듈(내가 손대지 않은 6/11 작성 파일)의 기존 이슈 — 이번 세션과 무관.
  • 바이너리 배치: 신규 빌드를 /tmp에 컴파일 후 mv로 3곳(native/CrownyBrowser, CrownyBrowser.app/Contents/MacOS/CrownyBrowser, CrownyAI.app/Contents/MacOS/CrownyBrowser) 배치, sha256 동일 확인:
522b7d9f33f82e8c77603f1805730561c608b26d0c3460d886c3783e6e4d6f94 (3곳 일치).

개선 전/후 비교표

시나리오개선 전개선 후
앱 1개 콜드 오픈(세션 최초, 프리워밍 없다고 가정)~140~150ms(프리워밍이 백그라운드에서 먼저 끝내므로) 실질 ~90~110ms
앱 1개 웜 재오픈~90~110ms~90~110ms (동일 — 병목은 VM+python 스폰 자체, 이번 범위에선 미해결)
세션 시작 후 여러 앱 순차 첫 오픈(12개) 체감 총 지연사용자가 클릭할 때마다 분산 체감 (합 ~3.3s를 조작에 끼워 체감)앱 시작 시 백그라운드 1회로 흡수, 사용자 조작 중 체감 0
renderAndReload 프로세스 홉NSTask→/bin/bash→scriptNSTask→script (1홉 감소, 측정상 노이즈)
stale-toau 함정(7개 스크립트)소스 mtime만 비교 — 컴파일러/VM 갱신 시 잠재 오동작 위험4항 가드 전체 적용 — 위험 제거

남은 것 (범위 밖 — 다른 에이전트 진행 중)

  • 진짜 근본 해결: 상주 VM 세션(매 상호작용마다 VM 프로세스 재기동 대신 세션 유지) — src/터미널상주서버.한선 프로토타입을 병렬 에이전트가 작업 중. 이게 완성되면 웜 경로 ~90~110ms의 대부분(VM 스폰 2회 ~35ms + python 스폰 ~44ms)을 제거할 수 있어 구조적 병목의 근본 해결이 된다.
  • python 페이지배선.py 자체의 글리프/RLE 연산 최적화(현재 ~22ms)는 이번 범위 밖.

관련 파일

  • /Users/ef/CrownyBrowser/native/프리워밍.sh (신규)
  • /Users/ef/CrownyBrowser/native/공통자가치유.sh (신규)
  • /Users/ef/CrownyBrowser/native/crowny-browser.m (프리워밍 배선 + renderAndReload 직접exec)
  • /Users/ef/CrownyBrowser/native/{설정,파일,북마크,날씨,시계,할일,뷰어,런처,계산기,터미널}렌더.sh (자가치유 통합)
  • /Users/ef/CrownyBrowser/src/앱목록.psv (프리워밍이 순회하는 레지스트리, 미변경)
  • /Users/ef/CrownyBrowser/native/m3-배선/페이지배선.py (분석만, 미변경 — python -S 이득 없음 확인)

크라우니코드 보고

lookup HIT 0건(재사용) / MISS 0건(생성 — 신규 bash 유틸 2개는 기존 렌더sh 패턴을 직접 참조/일반화한 리팩터링이라 crownycode-learn 조회 대상 아님, 순수 셸 글루 코드) / learn 추가 0건. 본 작업은 기존 셸 파이프라인(프로젝트 관례상 bash)의 리팩터링·조립이며 새 한선씨(.한선) 로직 생성은 없었음 — CLAUDE.md 원칙상 신규 실행로직이 아닌 빌드/배선 글루는 기존 셸 관례를 따름(도구/CLI 산출물 자체가 이미 .한선 파이프라인을 호출하는 얇은 래퍼).