메서드 및 블록 호출 횟수 계산
-
YJIT는 프로그램의 핫스팟을 찾기 위해 각 함수 또는 블록의 호출 횟수를 계산합니다.
-
jit_entry_calls라는 내부 카운터를 사용하여 이 횟수를 추적합니다. -
카운터가 특정 임계값(작은 프로그램은 30, Rails 같은 큰 프로그램은 120)에 도달하면, YJIT는 해당 코드 섹션을 머신 언어로 변환합니다.
-
이후 Ruby는 원본 YARV 명령어 대신 컴파일된 머신 언어 버전을 실행하여 성능을 향상시킵니다.
YJIT 블록의 작동 방식
-
YJIT는 컴파일 과정에서 생성된 머신 언어 명령어를 YJIT 블록에 저장합니다.
-
이 YJIT 블록은 Ruby 블록과 구별되며, 개별 YARV 명령어 또는 작은 범위의 YARV 명령어에 대한 머신 언어 시퀀스를 포함합니다.
-
이를 통해 YJIT는 프로그램의 동작에 맞춤화된 최적화된 코드를 생성하고 불필요한 코드 컴파일을 방지합니다.
-
예를 들어,
getlocal_WC_1및getlocal_WC_0와 같은 두 개의 YARV 명령어가 하나의 YJIT 블록으로 컴파일될 수 있습니다. -
이 블록 내부에는 M1 마이크로프로세서의 레지스터(x1, x9)에 값을 로드하는 ARM64 머신 언어 명령어가 포함됩니다.
YJIT 브랜치 스텁을 통한 타입 처리
-
YJIT가
opt_plus와 같은 YARV 명령어를 컴파일할 때, 덧셈 인수의 타입을 즉시 알 수 없는 문제에 직면할 수 있습니다. 머신 언어는 타입에 따라 다른 명령어를 요구하기 때문입니다. -
YJIT는 프로그램 전체를 미리 분석하여 가능한 모든 타입을 결정하는 대신, “브랜치 스텁”이라는 영리한 트릭을 사용합니다.
-
이는 블록이 실행될 때까지 기다렸다가 실제로 전달되는 인수의 타입을 관찰하는 “기다렸다 보는(wait-and-see)” 컴파일 동작을 가능하게 합니다.
-
초기에는 브랜치가 실제 블록이 아닌 스텁을 가리키도록 설정되며, 런타임에 타입 정보가 확인되면 적절한 머신 언어 블록으로 컴파일됩니다.