Skip to content

v0.0.160

Choose a tag to compare

@github-actions github-actions released this 22 Aug 08:12
· 115 commits to main since this release

rePIU v0.0.160

이전 릴리스 v0.0.159 이후의 변경입니다. 이번 릴리스는 실행 성능에 집중합니다. 번역된
RET이 대부분의 경우 host로 넘어가지 않고 스스로 목적지를 찾도록 바꿔, 같은 장면에서
프레임이 +59.4% 늘었습니다.

return 처리 비용을 먼저 재고, 다음에 없앴습니다

  • Task 482: return handler를 서로 겹치지 않는 다섯 단계(entry 검증, 게스트 스택
    target read와 bookkeeping, target 분류·resolution, megamorphic 정책·patch, guest
    continuation)와 residual로 나누는 opt-in 계측
    REPIU_AOT_RETURN_STAGE_PROFILE을 추가했습니다.
  • 그 계측이 기존 수치의 오류를 먼저 잡아냈습니다. 기존 kAotReturn bucket은
    HandleAotReturnTransfer 안만 재고 있어 adapter 구간이 통째로 빠져 있었습니다. 실제
    return 처리 비용은 guest-run의 16.5~17.5%가 아니라 **약 27%**였습니다(3회 재현,
    비 1.39).
  • 지배적인 단계는 없었습니다 — resolve 31.7%, patch 27.5%, entry 25.1%,
    read 8.3%, continuation 7.3%. 그리고 정책 관측의 99.4%가 megamorphic bypass로,
    거의 모든 return이 4-entry PIC로 수렴하지 못하는 site에서 발생하고 있었습니다.

Megamorphic direct-return table (기본 켜짐)

  • Task 499: 번역된 RET의 miss 경로 앞에서 공용 memo table을 조회해, 적중하면
    host 전환 없이 원본 RET과 같은 스택 효과로 돌아갑니다. 실패하면 기존 경로가 한
    바이트도 바뀌지 않은 채 이어집니다.
  • pumpit8 A/B(60초 × 3쌍, 계측 없이 토글만 변경)에서 프레임 37,385 → 59,586,
    +59.38%
    (623 → 993 fps)였고 두 집단의 범위가 겹치지 않았습니다. 프레임당 삼각형은
    +2.84%로 장면 동일성 기준(3%) 안이고 grDrawPoint는 여섯 실행 모두 정확히 같았습니다.
  • host return 왕복이 실행당 약 5,000만 회에서 약 2,900회로, return 처리 비용이
    guest-run의 25.63%에서 **0.13%**로 내려갔습니다.
  • REPIU_AOT_DIRECT_RETURN_TABLE=0으로 끄면 이 기능이 없던 때와 캐시 바이트가 정확히
    같아집니다
    (473,078 B 대 526,083 B). 표 크기는
    REPIU_AOT_DIRECT_RETURN_TABLE_BITS로 8~18(기본 15)을 줍니다.
  • 정확성은 새 불변식을 만들지 않습니다. table 적중은 4-entry PIC 적중과 의미가 같으므로,
    항목이 PIC 항목과 같은 조건에서만 살아 있으면 됩니다. 삽입은 host가 검증한 active hit만
    받고, 무효화는 PIC guard를 되돌리는 바로 그 자리에서 table 전체를 지웁니다. 그 지점은
    게스트 스레드가 이미 정지해 있어 lock도 generation stamp도 필요 없습니다.

Glide gate 비용의 실제 구성

같은 계측 작업에서 Glide gate(guest-run의 23.724.7%)도 3회 재현으로 분해했습니다.
최대 구성은 렌더링이 아니라 호스트에 닿지 않는 crossing으로, bucket의 45
47%
(guest-run의 10.711.6%)입니다. 게이트 진입의 **70.871.1%**가 setter elision으로
걸러지는 호출인데, elision은 호스트 왕복만 없앴을 뿐 crossing은 회당 약 1,790 cycle을
그대로 씁니다. 이 비용은 지금까지 측정된 적이 없습니다.

검증

  • Win32 x86 Debug/Release repiu_exe, repiu, repiu_aot_probe 빌드 성공.
  • repiu_aot_probe --return-stage-profile 8항목, --direct-return-table 5항목 통과.
    후자에는 emit한 바이트열을 실제로 실행해 적중 시 EIP·ESP가 원본 RET과 같고
    eax/ecx가 보존되는지 확인하는 항목이 포함됩니다(C3와 C2 iw 양쪽).
  • pumpit8 전체 probe Debug/Release 모두 exit 0.
  • 실행 중 page retirement 36회 동안 table 무효화가 정상 동작했고, 모든 실행에서 return
    fallback 0, index scan 0이었습니다.

알려진 제한

  • direct-return table의 승격 근거는 pumpit8 무인 attract 장면입니다. 게임플레이 장면에서
    한 번 더 확인해 두는 것이 좋습니다. 회귀가 보이면
    REPIU_AOT_DIRECT_RETURN_TABLE=0이 즉시 이전 동작으로 되돌립니다.
  • pumpitea 첫 구동에서 4.2초 만에 널 역참조 크래시 1건이 있었고 재현되지 않았습니다.
    마운트가 external-extent 파일 89개를 조용히 건너뛰는 것이 유력한 원인이며,
    pumpite CHD 마운트 분석에 재개 절차와 함께
    기록했습니다.
  • 다음 성능 축은 Glide입니다 — crossing 앞으로 elision 판정 옮기기(약 11%)와 호스트
    wake 지연(약 6%).

rePIU v0.0.160

Changes since v0.0.159. This release is about execution performance: a translated RET now
resolves its own destination in most cases instead of crossing to the host, which raised frames on
the same scene by 59.4%.

Measuring return cost before removing it

Task 482 added REPIU_AOT_RETURN_STAGE_PROFILE, an opt-in instrument that splits the return
handler into five mutually exclusive stages plus the residual of the same window. It immediately
corrected the number the project had been steering by: the old kAotReturn bucket measured only
the inside of HandleAotReturnTransfer and missed the adapter entirely, so return handling was
about 27% of guest-run, not the 16.5-17.5% previously recorded — a factor of 1.39,
reproduced across three runs. No stage dominated (resolve 31.7%, patch 27.5%, entry 25.1%,
read 8.3%, continuation 7.3%), and 99.4% of policy observations were megamorphic bypasses.

Megamorphic direct-return table, on by default

Task 499 probes a shared memo table ahead of the return miss path; a hit returns with the original
RET's own stack effect and no host transition, and a miss falls through into the existing path
unchanged. A pumpit8 A/B of three 60-second pairs, with no instruments and only the toggle
changing, measured 37,385 against 59,586 frames, +59.38% (623 against 993 fps) with
non-overlapping groups, triangles per frame within 2.84%, and identical grDrawPoint totals in all
six runs. Host return round trips fell from about fifty million per run to about 2,900, taking
return handling from 25.63% of guest-run to 0.13%. REPIU_AOT_DIRECT_RETURN_TABLE=0 restores
the exact cache bytes of a build without the feature, and
REPIU_AOT_DIRECT_RETURN_TABLE_BITS sizes the table from 8 to 18 bits, defaulting to 15.

Correctness adds no new invariant: a table hit is semantically identical to a four-entry PIC hit,
insertion takes only resolutions the host validated, and invalidation clears the table exactly
where PIC guards are reset, at a point where the guest thread is already stopped.

What the Glide gate actually costs

The same measurement work decomposed the Glide gate (23.7-24.7% of guest-run) across three runs.
Its largest component is not rendering but crossings that never reach the host thread, 45-47%
of the bucket and 10.7-11.6% of guest-run: 70.8-71.1% of gate entries are calls that setter
elision discards, and elision removed only the host round trip, leaving about 1,790 cycles of
crossing per call — a cost that had never been measured.

Verification

Debug and Release builds of all three binaries succeeded; the eight --return-stage-profile checks
and five --direct-return-table checks passed, the latter including one that executes the
emitted bytes
and confirms a hit reproduces the original RET's EIP and ESP with eax and ecx
preserved, for both C3 and C2 iw; the complete pumpit8 probe exited zero on both
configurations; and table invalidation behaved correctly across 36 live page retirements with zero
return fallbacks and zero index scans in every run.

Known limitations

The promotion rests on an unattended pumpit8 attract scene and deserves one gameplay-scene
confirmation; REPIU_AOT_DIRECT_RETURN_TABLE=0 reverts instantly if a regression appears. A first
pumpitea run crashed once on a null dereference after 4.2 seconds and did not reproduce; the mount
silently skipping 89 external-extent files is the leading suspect and the investigation is recorded
with its resumption steps in the
pumpite CHD mount analysis. The next performance axis
is Glide: moving the elision test ahead of the crossing, worth about 11% of guest-run, and host
wake latency, about 6%.