# FFI 속도 향상을 위한 Tiny JITs

> Clean Markdown view of GeekNews topic #19214. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=19214](https://news.hada.io/topic?id=19214)
- GeekNews Markdown: [https://news.hada.io/topic/19214.md](https://news.hada.io/topic/19214.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2025-02-14T08:37:06+09:00
- Updated: 2025-02-14T08:37:06+09:00
- Original source: [railsatscale.com](https://railsatscale.com/2025-02-12-tiny-jits-for-a-faster-ffi/)
- Points: 2
- Comments: 1

## Topic Body

### 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 헤드에서만 실행 가능.

## Comments



### Comment 34535

- Author: neo
- Created: 2025-02-14T08:37:06+09:00
- Points: 1

###### [Hacker News 의견](https://news.ycombinator.com/item?id=43030388) 
* Java Constraint Solver (Timefold)와 CPython 간의 함수 호출을 위해 FFI를 많이 다루어야 했음
  - FFI의 성능 문제는 주로 호스트 언어와 외국어 간의 통신을 위한 프록시 사용에서 발생함
  - JNI나 새로운 외국 인터페이스를 사용한 직접적인 FFI 호출은 빠르며, Java 메서드를 직접 호출하는 것과 비슷한 속도임
  - 그러나 CPython과 Java의 가비지 컬렉터는 잘 맞지 않아 동기화를 위해 특별한 기술이 필요함
  - JPype나 GraalPy와 같은 프록시를 사용하면 성능 오버헤드가 발생하며, 매개변수와 반환값을 변환해야 하고 추가적인 FFI 호출이 발생할 수 있음
  - CPython 객체를 Java로 전달하면 Java는 CPython 객체에 대한 프록시를 가짐
  - 그 프록시를 다시 CPython으로 전달하면 프록시의 프록시가 생성됨
  - 결과적으로 JPype 프록시는 CPython을 직접 FFI로 호출하는 것보다 1402% 느리고, GraalPy 프록시는 453% 느림
  - 최종적으로 CPython 바이트코드를 Java 바이트코드로 변환하고, 사용된 CPython 클래스에 해당하는 Java 데이터 구조를 생성함
  - 그 결과 프록시를 사용하는 것보다 100배 빠른 성능 향상을 얻음
  - CPython 바이트코드를 변환하거나 읽는 것은 매우 불안정하고 문서화가 부족하며, VM의 여러 특이점 때문에 다른 바이트코드로 직접 매핑하기 어려움
  - 자세한 내용은 블로그 게시물을 참조할 수 있음: [링크](https://timefold.ai/blog/java-vs-python-speed)

* Rails At Scale과 byroot의 블로그 덕분에 현재 Ruby 내부와 성능에 대한 심도 있는 논의에 관심을 가지기에 좋은 시기임
  - 최근 Ruby와 Rails의 개선 덕분에 Rubyist로서 좋은 시기임

* 외부 함수 호출을 위해 3rd party 라이브러리를 호출하는 대신 코드를 JIT 컴파일할 수 있는지에 대한 질문
  - LuaJIT FFI의 기본 원리라고 확신함: [링크](https://luajit.org/ext_ffi.html)
  - LuaJIT의 FFI가 매우 빠른 이유라고 생각함

* JVMCI를 사용하여 arm64/amd64 코드를 즉석에서 생성하여 JNI 없이 네이티브 라이브러리를 호출하는 라이브러리 관련 정보: [링크](https://github.com/apangin/nalim)

* "가능한 한 많은 Ruby를 작성하라, 특히 YJIT가 Ruby 코드는 최적화할 수 있지만 C 코드는 그렇지 않기 때문"이라는 의견
  - Ruby가 꽤 느린 언어가 아닌가 하는 의문
  - 네이티브로 들어간다면 가능한 한 많은 작업을 네이티브에서 하고 싶음

* 10년 이상 Ruby를 사용해왔고, 최근의 발전을 보는 것이 매우 흥미로움
  - 기대됨

* 왜 JIT 컴파일이 필요한지에 대한 의문
  - C로 작성할 수 있다면 로드 시점에 컴파일할 수 있지 않을까 하는 생각

* FFI - Foreign Function Interface, 즉 Ruby에서 C를 호출하는 방법

* 이것이 바로 libffi가 하는 일 아닌가 하는 질문

* tenderlovemaking.com으로 가지 않은 이유를 알 것 같음
