FFI 속도 향상을 위한 Tiny JITs 기술

4 weeks ago 6

CRuby의 FFI 속도 향상 가능성

  • Ruby에서 네이티브 코드를 호출해야 할 때, 가능한 한 많은 Ruby 코드를 작성하는 것이 좋음. YJIT는 Ruby 코드를 최적화할 수 있지만 C 코드는 최적화할 수 없기 때문임.
  • 네이티브 라이브러리를 호출할 때는 Ruby에서 대부분의 작업을 수행하고, 네이티브 함수 호출을 위한 간단한 API를 제공하는 네이티브 확장을 작성하는 것이 좋음.
  • FFI는 네이티브 확장만큼의 성능을 제공하지 않음. 예를 들어, strlen C 함수를 FFI로 래핑한 경우, C 확장과 비교하여 성능이 떨어짐.

벤치마크 결과

  • String#bytesize를 직접 호출하는 것이 가장 빠르며, 이는 기준점으로 생각할 수 있음.
  • C 확장을 통한 strlen 호출이 두 번째로 빠르고, 간접적으로 String#bytesize를 호출하는 것이 그 다음임.
  • FFI 구현은 가장 느림. 이는 FFI를 통한 네이티브 함수 호출 시 상당한 오버헤드가 발생함을 보여줌.

현실을 바꿀 수 있을까?

  • Chris Seaton의 아이디어로, 외부 함수를 호출하기 위해 JIT 코드를 생성할 수 있는 가능성을 탐색 중임.
  • FFI 래퍼 예제에서 attach_function 호출 시, 래퍼 함수 정의 시점에 필요한 기계 코드를 생성할 수 있음.

RJIT 활용

  • RJIT는 Ruby로 작성된 JIT 컴파일러로, Ruby와 함께 제공됨.
  • RJIT를 gem으로 추출하여 3rd 파티 JIT 컴파일러가 Ruby 데이터 구조를 쉽게 매핑할 수 있도록 함.
  • JIT 엔트리 함수 포인터를 항상 실행하여 3rd 파티 JIT가 기계 코드에 등록할 수 있도록 함.

개념 증명

  • "FJIT"라는 작은 개념 증명을 통해, 런타임에 기계 코드를 생성하여 외부 함수를 호출할 수 있음.
  • 벤치마크 결과, FJIT가 생성한 기계 코드는 C 확장보다 빠르며, FFI 호출보다 2배 이상 빠름.

결론

  • C 확장과 동일한 속도(또는 더 빠른 속도)를 유지하면서 가능한 한 많은 Ruby 코드를 작성할 수 있는 가능성을 보여줌.
  • Ruby가 FFI 없이 네이티브 코드를 호출할 수 있는 장점을 가질 수 있음.

주의사항

  • 현재 ARM64 플랫폼에만 제한됨. x86_64 백엔드를 추가해야 함.
  • 모든 매개변수 유형과 반환 유형을 처리하지 않음. 단일 매개변수와 반환만 처리 가능.
  • Ruby를 --rjit --rjit-disable 플래그로 실행해야 함. Kokubun의 기능이 적용되면 해결될 것임.
  • 현재 Ruby 헤드에서만 실행 가능.

Read Entire Article