[번역] What’s new in Ruby 3.3(Ruby 3.3의 새로운 기능)
매년 크리스마스에 Ruby의 새 버전을 Ruby Core 팀이 출시합니다. 올해도 다르지 않을 것 같으며, 다음 주에 Ruby 3.3이 출시될 것으로 예상됩니다.
올해 출시의 주요 핵심은 개발자 경험과 성능입니다. 이전 버전처럼 언어 기능에 큰 변화가 있는 것은 아니기 때문에 이번 포스팅은 평소보다 짧아질 예정입니다. 이번 출시의 주요 내용은 아래와 같습니다.
YJIT
Ruby의 JIT 컴파일러인 YJIT는 지난 몇 년 동안 놀라운 발전을 이루었습니다. 올해도 계속해서 속도는 빨라지고, 메모리 사용량은 줄었습니다.
Basecamp 및 Shopify와 같은 회사들에서는 이미 3.3의 Preview 버전을 프로덕션에 적용하였으며, YJIT가 없는 3.3.0에 비해 평균 응답 시간이 약 15% 향상되었습니다. 실제로, YJIT는 Rails 앱의 속도를 높이는 데 매우 효과적이어서 Ruby 3.3을 사용하는 경우 새로 생성된 Rails 앱에서 기본적으로 활성화됩니다.
YJIT를 런타임 시 활성화 할 수 있는 새로운 메서드인 RubyVM::YJIT.enable
가 도입되었습니다. 이 메서드는 앱을 빠르게 로드하고, 부팅한 후에 YJIT를 활성화하려는 경우 유용할 수 있습니다. 이것이 Rails 앱을 초기화 시 JIT를 활성화하는 데 사용하는 것입니다.
RJIT
실험적인 JIT 컴파일러인 RJIT가 도입되었습니다. 이것은 순수한 Ruby로 작성되었으며, Ruby 3.2에서 사용 가능한 MJIT의 대안입니다. RJIT는 YJIT보다 훨씬 더 많은 메모리를 사용하여 실험용으로만 존재합니다. 프로덕션에서는 항상 YJIT를 사용해야 합니다.
IRB
수년 동안 선호하는 디버깅 도구로 pry와 byebug를 사용하다가 지난 몇 달 동안 모든 나의 디버깅에 IRB를 사용하도록 전환하였습니다. 이번 출시에서 IRB와 내장된 debug gem의 통합이 크게 개선되어서 더 이상 pry와 byebug가 필요하지 않게 되었습니다.
Range
Range
클래스에 몇 가지 변화가 있습니다. overlap?
메서드가 추가되었습니다:
(1..3).overlap?(3..5) # true
(1..3).overlap?(4..6) # false
추가 정보) overlap? 메서드는 두 개의 범위를 비교하여 겹치는지 확인하는 메서드입니다. 현재까지 Rails에 내장된 메서드입니다.
또한, reverse_each
메서드는 시작이 없는 범위에서도 사용할 수 있습니다.
(..10).reverse_each.take(3) #=> [10, 9, 8]
추가 정보) reverse_each는 역순으로 탐색하는 메서드입니다.
Prism parser
Prism이라는 새로운 parser가 default gem으로 도입되었습니다. 에러에 더 강하며, ripper 대신 사용할 수 있습니다. 이는 Ruby 구현 전반에 걸쳐 사용 가능하며, 현재 MRI, JRuby, TruffleRuby, Sorbet에 통합되고 있습니다.
Prism을 사용하면, Prism.parse
메서드를 사용해 코드를 분석할 수 있고 아래와 같은 결과를 얻을 수 있습니다.
> Prism.parse("1 + 2")
#<Prism::ParseResult:0x000000010f5518c8
@comments=[],
@data_loc=nil,
@errors=[],
@magic_comments=[],
@source=#<Prism::Source:0x000000012aa77530 @offsets=[0], @source="1 + 2", @start_line=1>,
@value=
@ ProgramNode (location: (1,0)-(1,5))
├── locals: []
└── statements:
@ StatementsNode (location: (1,0)-(1,5))
└── body: (length: 1)
└── @ CallNode (location: (1,0)-(1,5))
├── flags: ∅
├── receiver:
│ @ IntegerNode (location: (1,0)-(1,1))
│ └── flags: decimal
├── call_operator_loc: ∅
├── name: :+
├── message_loc: (1,2)-(1,3) = "+"
├── opening_loc: ∅
├── arguments:
│ @ ArgumentsNode (location: (1,4)-(1,5))
│ ├── flags: ∅
│ └── arguments: (length: 1)
│ └── @ IntegerNode (location: (1,4)-(1,5))
│ └── flags: decimal
├── closing_loc: ∅
└── block: ∅,
@warnings=[]>
M:N thread scheduler
thread와 ractor의 성능을 향상시키기 위해 새로운 M:N thread scheduler 스케줄러가 도입되었습니다. 여기서 M은 ractor의 수이고, N은 기본 스레드 수입니다. (예: CPU 코어 수와 동일) 이를 통해 우리는 기본 스레드를 생성하는 오버헤드를 지불하지 않고, 각각 많은 ractor를 생성할 수 있습니다.
추가정보) ractor란? Ruby 3.0에서 도입된 병렬 처리를 위한 새로운 추상화입니다. Ractor는 Ruby의 "Actor-model"에서 파생된 이름으로, 병렬 실행을 가능하게 하는 독립적인 병렬 작업 단위를 의미합니다. Ractor는 각각 자체의 스레드를 가지며, 이 스레드는 다른 Ractor와 공유되지 않습니다. 이는 Ruby의 전역 VM 락(GVL) 문제를 해결하는 데 도움이 됩니다. GVL은 한 번에 하나의 스레드만 Ruby 코드를 실행할 수 있도록 제한하는데, 이로 인해 멀티 코어 CPU를 최대로 활용하는 것이 어렵습니다. Ractor를 사용하면, 각 Ractor가 자체 스레드를 가지므로 여러 Ractor가 동시에 실행될 수 있습니다. Ractor는 메시지 전달을 통해 통신합니다. 이는 Ractor 간에 상태를 공유하지 않고, 오직 메시지를 통해서만 데이터를 교환하도록 강제합니다. 이러한 특성은 병렬 프로그래밍에서 발생할 수 있는 데이터 경쟁 조건을 방지하는 데 도움이 됩니다. Ractor는 Ruby에서 병렬 처리를 위한 강력한 도구로, CPU 집약적인 작업을 병렬로 처리하거나, IO 집약적인 작업을 동시에 처리하는 데 유용합니다.
다른 변화
- Ruby 3.4에서 블록의 첫 번째 인자에 대한 참조로 사용되는 제안이 있습니다.
it
인자 없이 호출되는 것은 deprecated가 될 예정이며, 경고가 표시됩니다. - Bison은 유지 관리성을 향상시키기 위해 Lrama parser 생성기로 대체되었습니다.
- 더욱 성능을 향상시키기 위해 GC가 많이 최적화되었습니다.
더 읽어보기
이 포스트를 통해 가장 흥미롭다고 생각하는 변경 사항 중 일부를 강조하려고 노력하고 종종 사용하지 않을 수 있는 기능은 건너뜁니다. 출시에 대해 보다 포괄적으로 살펴보려면 공지 및 변경 로그를 살펴보는 것이 좋습니다.
'Backend > Ruby' 카테고리의 다른 글
ruby에서의 Enumerable#tally란? (0) | 2025.01.07 |
---|---|
[번역] Ruby Modules(루비 모듈) (4) | 2024.09.20 |
ruby의 ?(Question Mark / Boolean)의 의미 (0) | 2023.11.25 |
ruby의 !(Exclamation mark / Bang)의 의미 (4) | 2023.11.24 |
ruby의 배열내의 카운트를 효과적으로 세는 법 (3) | 2023.11.17 |