1P by neo 2달전 | favorite | 댓글 1개

Ruby 3.3.0 출시

  • Ruby 3.3.0 버전이 출시됨. 새로운 파서인 Prism 도입, 파서 생성기로 Lrama 사용, 순수 Ruby로 작성된 JIT 컴파일러 RJIT 추가, 특히 YJIT의 성능 개선이 이루어짐.

Prism 파서

  • Prism은 Ruby 언어를 위한 이식 가능하고, 오류에 강하며, 유지보수가 용이한 재귀 하향 파서로 기본 gem으로 제공됨.
  • Prism은 생산 환경에 적합하며 활발히 유지보수되고 있으며, Ripper를 대체하여 사용 가능함.
  • Prism 사용법에 대한 상세한 문서 제공됨.
  • Prism은 CRuby 내부에서 사용되는 C 라이브러리이자 Ruby 코드를 파싱할 필요가 있는 모든 도구에서 사용할 수 있는 Ruby gem임.
  • Prism API의 주요 메소드로는 Prism.parse(source), Prism.parse_comments(source), Prism.parse_success?(source) 등이 있음.
  • Prism 저장소에 직접 pull request나 이슈를 제출하여 기여할 수 있음.
  • Prism 컴파일러를 실험적으로 사용하려면 ruby --parser=prism 또는 RUBYOPT="--parser=prism"를 사용할 수 있으나, 디버깅 목적으로만 사용해야 함.

Lrama 파서 생성기

  • Bison을 Lrama LALR 파서 생성기로 대체함.
  • Ruby의 파서에 대한 미래 비전을 참조하면 관심 있는 사람들이 확인할 수 있음.
  • 유지보수를 위해 내부 Lrama 파서가 Racc에 의해 생성된 LR 파서로 교체됨.
  • 파라미터화 규칙 (?, *, +) 지원, Ruby parse.y에서 사용될 예정임.

YJIT

  • Ruby 3.2 대비 주요 성능 개선 사항이 있음.
  • splat 및 rest 인자에 대한 지원이 개선됨.
  • 가상 머신의 스택 작업에 대해 레지스터가 할당됨.
  • 선택적 인자가 있는 더 많은 호출이 컴파일됨. 예외 처리기도 컴파일됨.
  • 지원되지 않는 호출 유형 및 메가모픽 호출 사이트는 더 이상 인터프리터로 나가지 않음.
  • Rails의 #blank? 및 특수한 #present?와 같은 기본 메소드가 인라인 처리됨.
  • Integer#*, Integer#!=, String#!=, String#getbyte, Kernel#block_given?, Kernel#is_a?, Kernel#instance_of?, Module#=== 등이 특별히 최적화됨.
  • 컴파일 속도가 Ruby 3.2보다 약간 빨라짐.
  • Optcarrot에서 인터프리터보다 3배 이상 빠름!
  • Ruby 3.2 대비 메모리 사용량이 크게 개선됨.
  • 컴파일된 코드의 메타데이터가 훨씬 적은 메모리를 사용함.
  • --yjit-call-threshold가 30에서 40,000개 이상의 ISEQ가 있는 애플리케이션의 경우 120으로 자동으로 상승됨.
  • --yjit-cold-threshold가 추가되어 냉각된 ISEQ의 컴파일을 건너뜀.
  • Arm64에서 더 컴팩트한 코드가 생성됨.
  • 코드 GC는 기본적으로 비활성화됨.
  • --yjit-exec-mem-size는 새 코드 컴파일이 중단되는 하드 리밋으로 처리됨.
  • 코드 GC로 인한 성능 저하가 없으며, Pitchfork를 사용하여 서버가 리포킹할 때 더 나은 복사-쓰기 동작을 보임.
  • 원하는 경우 --yjit-code-gc로 코드 GC를 활성화할 수 있음.
  • RubyVM::YJIT.enable을 추가하여 실행 시간에 YJIT를 활성화할 수 있음.
  • Rails 7.2는 이 방법을 사용하여 기본적으로 YJIT를 활성화할 예정임.
  • 애플리케이션이 부팅을 완료한 후에만 YJIT를 활성화하려면 이 방법을 사용할 수 있음.
  • 부팅 시 YJIT를 비활성화하면서 다른 YJIT 옵션을 사용하려면 --yjit-disable을 사용할 수 있음.
  • 기본적으로 더 많은 YJIT 통계가 제공됨.
  • yjit_alloc_size 및 여러 메타데이터 관련 통계가 기본적으로 제공됨.
  • --yjit-stats에 의해 생성된 ratio_in_yjit 통계가 릴리스 빌드에서 사용 가능함. 특별한 통계 또는 개발 빌드가 더 이상 필요하지 않음.
  • 더 많은 프로파일링 기능이 추가됨.
  • --yjit-perf가 Linux perf와 함께 프로파일링을 용이하게 하기 위해 추가됨.
  • --yjit-trace-exits--yjit-trace-exits-sample-rate=N을 사용한 샘플링을 지원함.
  • 더 철저한 테스트와 다수의 버그 수정이 이루어짐.

RJIT

  • 순수 Ruby로 작성된 JIT 컴파일러 RJIT이 도입되고 MJIT이 대체됨.
  • RJIT은 Unix 플랫폼의 x86-64 아키텍처에서만 지원됨.
  • MJIT과 달리, 런타임에 C 컴파일러가 필요하지 않음.
  • RJIT은 실험적인 목적으로만 존재함.
  • 생산 환경에서는 YJIT을 계속 사용해야 함.
  • Ruby JIT 개발에 관심이 있는 경우, RubyKaigi의 3일차에 있는 k0kubun의 발표를 확인할 것을 권장함.

M:N 스레드 스케줄러

  • M:N 스레드 스케줄러가 도입됨.
  • M Ruby 스레드가 N 네이티브 스레드(운영 체제 스레드)에 의해 관리되므로 스레드 생성 및 관리 비용이 감소함.
  • M:N 스레드 스케줄러는 C 확장과 호환성을 깰 수 있으므로 기본적으로 메인 Ractor에서 비활성화됨.
  • RUBY_MN_THREADS=1 환경 변수를 사용하여 메인 Ractor에서 M:N 스레드를 활성화할 수 있음.
  • 비메인 Ractor에서는 항상 M:N 스레드가 활성화됨.
  • RUBY_MAX_CPU=n 환경 변수는 N의 최대 개수(네이티브 스레드의 최대 개수)를 설정함. 기본값은 8임.
  • Ractor당 하나의 Ruby 스레드만 실행될 수 있으므로, 싱글 Ractor 애플리케이션(대부분의 애플리케이션)은 1개의 네이티브 스레드만 사용함.
  • 차단 작업을 지원하기 위해 N보다 많은 네이티브 스레드가 사용될 수 있음.

성능 개선

  • defined?(@ivar)가 Object Shapes를 사용하여 최적화됨.
  • Socket.getaddrinfo와 같은 이름 해석이 이제 인터럽트될 수 있음(사용 가능한 환경에서 pthreads가 있는 경우).
  • 가비지 컬렉터에 대한 여러 성능 개선 사항이 있음.
    • 젊은 객체가 노인 객체에 의해 참조되면 즉시 노인 세대로 승격되지 않아 주요 GC 수집 빈도가 크게 감소함.
    • 주요 GC 수집을 유발하는 보호되지 않은 객체의 수를 제어하는 새로운 REMEMBERED_WB_UNPROTECTED_OBJECTS_LIMIT_RATIO 조정 변수가 도입됨. 기본값은 0.01(1%)로 설정되어 주요 GC 수집 빈도가 크게 감소함.
    • Write Barriers가 누락된 많은 핵심 유형에 대해 구현됨. 이로 인해 소규모 GC 수집 시간과 주요 GC 수집 빈도가 크게 감소함.
    • 대부분의 핵심 클래스가 이제 Variable Width Allocation을 사용함. 이로 인해 이러한 클래스의 할당 및 해제가 더 빨라지고 메모리 사용량이 줄어들며 힙 단편화가 감소함.
    • 가비지 컬렉터에 약한 참조 지원이 추가됨.

기타 주목할 만한 변경 사항

  • IRB는 고급 irb:rdbg 통합, ls, show_source 및 show_cmds 명령에 대한 페이저 지원, ls 및 show_source 명령에 의해 제공되는 정보의 정확성 및 유용성 향상, 타입 분석을 사용한 실험적 자동 완성 등을 포함하여 여러 개선 사항을 받음.
  • IRB는 또한 향후 개선을 용이하게 하기 위해 광범위한 리팩토링을 거치고 수십 개의 버그 수정을 받음.

호환성 문제

  • 인자 없이 블록 내에서 it 호출은 더 이상 사용되지 않으며, Ruby 3.4에서는 첫 번째 블록 매개변수를 참조하게 됨.
  • 사용되지 않는 환경 변수가 제거됨.

표준 라이브러리 업데이트

  • RubyGems과 Bundler는 사용자가 Gemfile이나 gemspec에 추가하지 않고 다음 gem을 요구할 경우 경고를 표시함. 이는 해당 gem들이 미래 버전의 Ruby에서 번들 gem이 될 예정이기 때문임.
  • 다음과 같은 기본 gem이 추가되거나 업데이트됨: prism 0.19.0, RubyGems 3.5.3, abbrev 0.1.2 등 다수.
  • 다음과 같은 번들 gem이 기본 gem에서 승격되거나 업데이트됨: racc 1.7.3, minitest 5.20.0 등 다수.

GN⁺의 의견

  • Prism 파서 도입: Ruby 3.3.0의 가장 중요한 특징 중 하나는 새로운 Prism 파서의 도입이다. 이는 Ruby 코드를 더 효율적으로 파싱하고, 오류에 강하며, 유지보수가 쉬운 파서를 제공함으로써 Ruby 개발자들에게 큰 도움이 될 것이다.
  • YJIT의 성능 개선: YJIT의 주요 성능 개선은 Ruby 애플리케이션의 실행 속도를 크게 향상시킬 것이며, 특히 메모리 사용량 감소와 GC 최적화는 대규모 Ruby 애플리케이션의 성능과 안정성에 긍정적인 영향을 미칠 것이다.
  • M:N 스레드 스케줄러: M:N 스레드 스케줄러의 도입은 멀티스레딩 Ruby 애플리케이션의 성능을 개선할 수 있는 잠재력을 가지고 있다. 이는 스레드 관리 비용을 줄이고, 더 효율적인 병렬 처리를 가능하게 할 것이다.
Hacker News 의견
  • Ruby 3.3의 등장으로, 개발자의 행복을 중시하는 언어인 Ruby가 이전의 느린 이미지를 벗어나 빠른 속도를 자랑함.

    • YJIT 기술과 객체 형태, GC 최적화 등의 혁신을 통해 Ruby의 성능이 크게 향상됨.
    • Shopify와 같은 대형 Ruby 사용 업체들이 Ruby 3.3의 성능 개선을 경험하고 있음.
    • Ruby의 미래에 대해 개인적으로 매우 기대하고 있으며, Ruby 3.3을 고객의 프로덕션 사이트에 적용하는 데 기대감을 표함.
  • Ruby 3.3은 지난 10년간 가장 중요하고 기능이 풍부한 릴리스로, Python보다 먼저 JIT를 출시한 것에 대해 놀라움을 표함.

    • Prism, Lrama, IRB 등 다양한 기능들이 이전 해커뉴스 제출에서 논의됨.
    • Ractor, M:N 스레드 스케줄러, Fibre, Async와 같은 기능들이 Rails의 맥락에서 충분히 언급되지 않았으며, 이 기능들을 프로덕션에서 사용하는 사람들의 경험을 듣고 싶어함.
  • Heroku에서 Ruby 3.3을 사용할 수 있음을 알림.

  • 매년 크리스마스마다 Ruby 언어는 새로운 릴리스를 출시함.

  • Python과 NodeJS를 이미 알고 있는 경우, Ruby를 배우는 것이 가치가 있는지에 대한 질문을 함. Ruby를 매력적이지만 어렵게 느낌.

  • Socket.getaddrinfo와 같은 이름 해석이 중단될 수 있음. 이름 해석이 필요할 때마다 워커 pthread를 생성하고 getaddrinfo(3)를 실행함.

    • 다른 언어 런타임도 비슷한 작업을 하는지에 대한 질문을 함. 스레드 생성이 무겁게 느껴질 수 있지만, 벤치마크에 따르면 오버헤드는 최소화되어 있음.
  • Prism이 흥미로움. Ruby 코드 분석 도구로 Prism을 사용하는 예가 있는지에 대한 질문을 함.

  • RUBY_MAX_CPU=n 환경 변수가 네이티브 스레드의 최대 수를 설정함. 기본값은 8임.

    • 기본값이 논리 코어의 수와 같아야 하는지에 대한 의문을 제기함. Rust의 Tokio와 많은 다른 M:N 런타임들처럼.
  • Prism을 사용한 좋은 예제에 대한 링크를 찾고 있음. 릴리스 페이지에서 "주목할 만한 API" 외에는 별다른 것을 보지 못해 실망함을 표함.

  • 완벽한 크리스마스 선물이라고 언급함.