JIT 실적용 3종 실측 — 3D렌더러·유체시뮬·ARIMA
개요
~/.crownycode/JIT적용후보.psv(haiku 스캔, 30~60배/20~50배/15~40배 가속 예측) 상위 3종에
대해 벤치 드라이버(/tmp/JIT실적용/*_벤치.한선, 원본 라이브러리 무수정·가져오기만)를 작성해
①공유 crownyc(기준) ②crownyc-jit(JIT off) ③CROWNY_JIT=1 crownyc-jit ④
CROWNY_JIT=1 CROWNY_JIT_STENCIL=1 4경로를 실측했다.
결론 요약 (표)
| 이름 | 기준(s) | JIT off(s) | JIT on(s) | +STENCIL(s) | 배속 | identical | BAIL 원인 |
|---|---|---|---|---|---|---|---|
| 3D렌더러(행렬곱) | 3.88 | 3.89 | 6.96 | 4.54 | 0× (오히려 0.56~0.85배, 44~15%↓) | YES | compiled=0/bailed=12 |
| 유체시뮬(압력해결)* | 0.20 | 0.17 | 0.85 | 0.62 | 0× (0.24~0.32배) | YES | compiled=0/bailed=1 |
| ARIMA(AR적합) | 0.32 | 0.33 | 1.02 | 0.81 | 0×** (0.32~0.41배) | YES | compiled=0/bailed=2 |
세 후보 전부 compiled=0 — JIT가 단 하나의 루프도 네이티브 컴파일하지 못했다. bailed>0,
native_fires=0. 스캔이 예측한 15~60배 가속은 실측상 0배이고, JIT를 켜면 오히려
15~76% 느려진다(아래 원인 분석 참조). byte-identical 확인은 3/3 전부 통과 —
BAIL이 발생해도 출력이 오염되지 않는 안전장치(런타임 differential 게이트)는 정상 작동했다.
왜 0배인가 — JIT 트랜스파일러의 근본적 범위 제한
crownyc_jit_hot.c의 핫루프 트랜스파일러(cj_transpile_loop)는 루프 본문에서 CALL(op 247)을
만나면 잎함수(leaf function) 인라인만 허용하고, 그 잎함수 몸체 안에 다시 CALL(배열/맵 접근
포함)이 있으면 그 잎함수 자체가 "미지원"으로 판정돼 상위 루프까지 통째로 BAIL된다
(cj_transpile_leaf 스위치의 default: return 0; /* 미지원(247 CALL 포함) = BAIL */).
세 후보 모두 배열/맵 원소 접근을 원소()/설정()/맵꺼내() 같은 빌트인 호출(그리고 3D렌더러는
그 위에 행렬원소()/행렬설정() 같은 사용자 함수 한 겹 더)로 수행한다. 즉 세 라이브러리의
"수치 핵심 루프"는 전부 아래 셋 중 하나에 해당:
- 3D렌더러
행렬곱: r/c/k 삼중루프 안에서행렬원소(A,r,k)같은 호출 — 사용자함수가 다시
원소() 호출(CALL 안에 CALL) → 리프 판정 실패 → BAIL.
- 유체시뮬
압력해결: 픽셀별로숫자변환(문자열변환(압력배열[idx-1]))— 배열 인덱싱 자체가
- ARIMA
AR적합: 매 반복숫자변환(문자열변환(데이터[i-j-1]))동일 패턴 → BAIL.
JIT 켜면 오히려 느려지는 이유
CROWNY_JIT=1 최초 1회 부팅 시 self-test가 /usr/bin/cc를 posix_spawn으로 실제 호출해
검증한다 — 이것만으로 고정 비용 ~0.5초 real time(user 0.05s, real 0.52s, 트리비얼 프로그램
실측)이 발생한다. 여기에 매 루프 반복마다 백엣지 카운터 갱신·해시테이블 조회 등 계측 오버헤드가
누적되며, BAIL 판정 자체도 (비록 /usr/bin/cc 재호출은 없지만) 트랜스파일 시도 문자열 빌드 비용을
문다. 결과적으로 세 후보 전부 real 시간이 기준 대비 1.3~1.8배 증가했다(user 시간은 거의 동일).
STENCIL 옵션도 동일 원인(compiled=0)이라 개선 없음.
실측 중 발견한 별개 결함(정직 기록)
- 유체시뮬 문자열핸들 480,000 캡 초과 크래시: 스캔이 지정한 원 규모(24x24 격자,
압력해결(유체,900))를 그대로 돌리면 [VM] 문자열 핸들 상한(480000) 초과 — 풀 누수 의심 후
무출력 종료(exit 1). 원인은 압력해결 내부의 숫자변환(문자열변환(배열[...])) 패턴이 셀당
5회 문자열 핸들을 생성하기 때문 — (22×22)×900×5 ≈ 2,178,000(캡의 4.5배). 16×16×150으로
줄이면 147,000(캡의 31%)로 안전. 이 캡 구조 때문에 유체시뮬/ARIMA는 인터프리터 5~30초대
벤치 자체가 원천적으로 불가능(캡 여유 최대치로 밀어붙여도 실측 ceiling은 약 0.3~0.5초).
- ARIMA AR적합 경사하강 발산 → SIGBUS(exit 138):
AR적합은 학습률=100 고정·정규화 없음·
ARIMA_stability_test.한선,
ARIMA_tiny.한선로 격리 재현)에서도 50에포크 내 계수가 발산해 정수 곱셈 오버플로가 폭주하고
결국 SIGBUS로 크래시한다. 임의의 비영(非零) 실데이터는 구조상 전부 발산 궤도로 판단됨 —
전부-0 입력(그래디언트가 항상 0으로 유지됨)으로만 안정 실행 가능(통계적으로 무의미하나 루프
반복 횟수·구조는 데이터값과 무관하게 동일해 벤치 목적엔 부합).
- ARIMA 잔차분석()의 표준편차() 호출이 ARIMA예측() 이후 항상 SIGBUS:
ARIMA예측()을 먼저
잔차분석(모델)(내부에서 표준편차(잔차들) 호출)을 실행하면 데이터가 전부 0이어도
100% 재현되는 SIGBUS(exit 138). 평균()만 단독 호출하면 안전 — 표준편차()가 트리거.
/tmp/JIT실적용/ARIMA_dbg{3,4,5,6}.한선으로 단계적 이분 격리해 원인을 표준편차() 호출
시점으로 좁혔으나 근본 원인(추정: 힙/맵 상태 오염)은 미규명. JIT와 무관한 별개 결함이라
벤치에서는 제외(사용 안 함)하고 정직 기록만 남김.총평 (2줄)
haiku 스캔이 예측한 15~60배 가속은 세 후보 전부 실측 0배(compiled=0, 전량 BAIL)이며, JIT를 켜면 self-test 고정비용+계측 오버헤드로 오히려 15~76% 느려진다 — 이 JIT는 배열/맵을 만지지 않는 순수 스칼라 루프에만 적용 가능해 "수치 라이브러리"라는 후보군 선정 기준 자체가 이 JIT의 실제 적용 범위와 어긋난다. 다만 byte-identical 3/3(런타임 differential 게이트 정상)과, 벤치 과정에서 드러난 유체시뮬/ARIMA의 VM 캡·발산·SIGBUS 결함 3건은 JIT 채택 여부와 무관하게 별도 수정이 필요한 실사용 리스크로 값어치가 있다.
관련 파일
- 벤치 드라이버(신규, 원본 무수정):
/tmp/JIT실적용/3D렌더러_벤치.한선,
/tmp/JIT실적용/유체시뮬_벤치.한선, /tmp/JIT실적용/ARIMA_벤치.한선
- 디버그/격리 재현 스크립트:
/tmp/JIT실적용/ARIMA_stability_test.한선,ARIMA_tiny.한선,
ARIMA_dbg1~6.한선, dbg_avg.한선
- 대상 라이브러리(무수정, 검증만):
/Users/ef/CrownyOS/crownyc/pkg/libs/3D렌더러.한선,
유체시뮬.한선, ARIMA.한선 (동일 내용이 /Users/ef/CrownyOS/crownyc/libs/에도 존재)
- JIT 구현:
/Users/ef/CrownyOS/crownyc/crownyc_jit_hot.c(핫루프 트랜스파일러),
crownyc_jit.c(부팅 self-test), crownyc_jitmain.c(빌드 원본), 바이너리 crownyc-jit
- 후보 원본:
~/.crownycode/JIT적용후보.psv
크라우니코드 준수
lookup: MISS 3건(행렬곱 벤치/압력해결 벤치/AR적합 벤치 검색, 기존 패턴 없음) → learn 추가 3건
(JIT벤치_행렬곱반복, JIT벤치_압력해결반복, JIT벤치_AR적합반복).
잔여 이슈
- 유체시뮬/ARIMA는 문자열핸들 480,000 캡 때문에 "인터프리터 5~30초" 규모 벤치 자체가 불가능 —
숫자변환(문자열변환(x)) 배열읽기 관용구를 원소 타입이
보장되는 직접 인덱싱으로 바꿔야 하나, 이번 작업은 "라이브러리 원본 수정 금지" 지시로 손대지 않음.
- ARIMA
AR적합경사하강 발산과표준편차()이후호출 SIGBUS는 라이브러리/VM 버그로 별도 티켓화 필요. - JIT가 배열/맵 루프를 지원하려면
cj_transpile_leaf의 CALL-안-CALL 판정과 배열 원소 접근(ARRAY_GET