
# 벤치마크

Phasis는 디스패치에 병목이 있는 JavaScript에서 **V8보다 대략 100배 느립니다**. 이는 PHP 기반 인터프리터와 네이티브 JIT 컴파일러 사이에 내재한 성능 차이입니다. 임베더가 물어야 할 것은 "V8와 비교하면 어떤가?"가 아니라 "내 작업에 충분히 빠른가?"입니다. 대부분의 임베딩 워크로드에서는 충분히 빠릅니다.

## 성능 한도가 문제가 되지 않는 경우

- **템플릿 처리** — 일반적인 페이지를 SSR 방식으로 렌더링하는 데 10 ms도 걸리지 않습니다.
- **검증 규칙** — 폼 검증용 사용자 제공 JS를 마이크로초 단위로 처리합니다.
- **데이터 변환** — 수백 개 항목에 대한 `array.map(transform)`를 수십 밀리초 안에 완료합니다.
- **샌드박스 실행기** — 호스트가 1초 제한을 두는 짧은 사용자 스크립트도 충분한 여유를 둡니다.

## 성능 한도가 문제가 되는 경우

- **계산량이 많은 내부 루프** — 이미지 처리, 대규모 배열 축소, 시뮬레이션 등입니다. 계산을 네이티브 PHP 라이브러리로 옮기고 [호스트 함수](https://phasis.inline0.dev/ko/docs/interop/host-functions)로 노출하세요.
- **실시간 애니메이션** — 60 fps를 유지해야 하는 작업입니다. 이런 JS는 임베디드 엔진이 아니라 클라이언트(브라우저, 네이티브 앱)에서 처리해야 합니다.
- **계산량이 많은 알고리즘을 JS로 재구현하는 경우** — 암호화, 압축, 파싱 등이 해당합니다. PHP에는 1000배 더 빠른 네이티브 구현이 있으므로 이를 사용하세요.

올바른 관점은 다음과 같습니다. Phasis는 PHP 형태의 애플리케이션에 임베드된 JS 형태의 로직을 위한 **접착 계층**입니다. PHP 호스트는 계산을 처리하고, JS 계층은 구성, 분기, 간단한 변환을 처리합니다.

## 순진한 트리 워커보다 엔진이 빠른 이유

- **바이트코드 VM** — 대부분의 핫 디스패치는 AST가 아니라 `VM::execute`의 opcode 스위치를 통해 실행됩니다.
- **사용자 지정 호출 스택** — 모든 JS 호출은 상태를 `VM::execute` 안에 유지하고 호출자 프레임을 풀에 저장합니다. JS 호출마다 새로운 PHP 프레임을 만들지 않습니다. [바이트코드 VM § 사용자 지정 호출 스택](https://phasis.inline0.dev/ko/docs/advanced/bytecode-vm)을 참조하세요.
- **인라인 캐시** — PC별 단형 캐시는 프로토타입 체인을 순회하지 않고 인스턴스의 프로토타입 메서드 읽기(`c.method()` 형태)를 처리합니다.
- **프레임 스냅샷 제너레이터** — 간단한 제너레이터 본문은 PHP Fiber를 거치는 대신 디스패치 상태를 힙 객체에 캡처해 일시 중단합니다.
- **JsToPhp 트랜스파일러** — 숫자 연산 또는 로컬 변수 사용이 많은 본문(`obj.method()`, `new Ctor()`, `this.member` 읽기 포함)은 PHP 클로저로 컴파일되며, PHP 8.5의 추적 JIT가 네이티브 기계어로 변환합니다.
- **JSON.stringify 캐시** — `JSON.stringify(o)`는 전이적 버전 무효화를 적용해 입력 객체에 출력을 캐시하므로, 딥 클론 관용구인 `JSON.parse(JSON.stringify(o))`의 속도를 높입니다.

## 직접 워크로드 측정하기

```bash
php bench/run.php
```

이는 `bench/microbench.js`에 포함된 마이크로벤치마크 모음을 실행합니다. 루프 산술, 재귀 함수, 객체 생성, 프로토타입 메서드 호출, 배열 push, JSON 왕복 등 몇 가지 합성 워크로드를 수행하고 테스트별 중앙값 벽시계 시간을 출력합니다. 기본 동작을 확인할 때 유용하며, 현재 수치는 모든 CI 벤치마크 실행 때마다 [`BENCH.md`](https://github.com/inline0/phasis/blob/main/BENCH.md)에 커밋됩니다.

직접 작성한 코드에는 아무 스크립트나 넣으세요:

```bash
php -d xdebug.mode=off bench/run.php path/to/your-script.js
```

PHP 수준의 프로파일링에는 Xdebug의 프로파일러를 사용할 수 있습니다. 다른 PHP 애플리케이션과 마찬가지로 Phasis에서 작동합니다. `xdebug.mode=profile`에서 스크립트를 실행한 뒤 cachegrind 출력을 살펴 어떤 `Phasis\` 클래스가 주된 비용을 차지하는지 확인하세요.

## 로드맵

실현 가능성이 높은 순서대로, V8 격차를 더 줄일 수 있는 작업은 다음과 같습니다:

- **다형성 인라인 캐시** — 하나의 `LOAD_MEMBER` 호출 지점에 느린 디스패치로 전환하기 전까지 캐시된 셰이프를 2-4개 저장합니다. 서로 다른 리시버를 처리하는 코드에 도움이 됩니다.
- **`yield*` 및 프레임 스냅샷 생성기 경로의 try-with-yield** — 현재 이 셰이프는 Fiber 기반 트리 워커로 전달됩니다. 스냅샷을 지원하려면 각각 명시적인 상태 머신 처리(위임된 이터레이터 전달, suspend 중 핸들러 스택 보존)가 필요합니다.
- **스냅샷의 비동기 생성기 및 `await`** — 사양은 생성기 중단과 마이크로태스크 큐를 함께 구성합니다. 바이트코드 본문이 `yield`와 `await`를 모두 지원하면 비동기 생성기도 스냅샷을 지원할 수 있습니다.
- **JsToPhp의 매개변수 타입 추론** — 함수 매개변수는 기본적으로 숫자 타입이므로 `function (obj) { obj.method() }`에서 컴파일러가 중단됩니다. 본문에서의 사용 방식을 바탕으로 객체 타입 매개변수를 추론하면, `new`를 통해 리시버를 함수 내부에서 직접 생성하지 않아도 함수 인수의 메서드를 호출할 수 있습니다.
- **직접 opcache 출력** — opcache 형식의 opcode를 직접 작성해 JsToPhp의 `eval()` 단계를 건너뜁니다. 가장 난도가 높은 작업이며, 나머지는 기존 아키텍처 안에서 구현할 수 있습니다.

이러한 작업은 Phasis이 대상으로 삼는 임베딩 워크로드에 필요하지 않습니다.
