발표는 Ruby 호출의 복잡성을 다양한 관점에서 분석합니다. 인자 전달은 단순히 파싱 단계를 넘어 메서드 정의 단계를 포함하며, 호출자(caller)에서 피호출자(callee)로의 전환 과정에서 발생하는 작업을 중심으로 분류합니다. 특히 C Ruby의 구현 세부 사항과 언어 의미론을 혼합하여 설명합니다.
Ruby 호출 분류의 기준
- 인자 전달 작업의 본질: 호출자가 인자를 준비하고 피호출자가 이를 받는 과정에서 발생하는 작업량을 기준으로 분류합니다.
- 호출자와 피호출자 간의 전환: 파싱 및 메서드 정의 단계를 넘어 실제 인자가 전달되는 시점의 동작에 초점을 맞춥니다.
데이터 가변적 기능 및 변환
- 스플랫(Splat) 및 키워드 스플랫:
*args
와**kwargs
와 같이 인자가 배열이나 해시가 아닐 경우,to_a
,to_hash
와 같은 변환 메서드를 호출하여 인자를 변환하는 프로토콜이 작동합니다. 이 과정에서 추가적인 메서드 호출이 발생하거나 특수한 동작이 나타날 수 있습니다. - nil 처리 및 변환 실패 시 특수 동작:
nil
을 스플랫할 경우 아무것도 전달되지 않는 특성이 있습니다. 배열 스플랫의 경우 변환에 실패하면 인자를 스플랫하지 않은 것처럼 원래 인자를 그대로 전달하는 독특한 동작을 보입니다. - 블록 인자 및 리파인먼트(Refinements):
%
심볼 단축 문법(&:method
)은 단순히to_proc
을 호출하는 것 이상으로 작동합니다. 특히 리파인먼트 스코프 내에서to_proc
을 재정의하거나 호출할 때, 렉시컬 스코프의 한계로 인해 예상치 못한 동작이 발생하여Symbol#to_proc
이 Ruby로 완벽하게 재구현되기 어렵다는 점을 보여줍니다.
숨겨진 데이터 전달
- 선택적 키워드 인자: 선택적 키워드 인자를 가진 메서드 호출 시, 호출자는 전달되지 않은 키워드에 대한 기본값 계산을 위해 숨겨진 정수 또는 해시를 함께 전달합니다. 이는 직접 접근할 수 없는 내부 데이터입니다.
- 포워딩 가능한 인자(Forwardable Parameters): Ruby 3.4부터 GC 할당을 줄이기 위해 도입된 기능으로, 메서드 인자를 다른 메서드로 포워딩할 때 호출 사이트의 특징을 담은
call info
라는 숨겨진 데이터가 함께 전달됩니다. 이 데이터는 중간 단계에서 인자 전달의 특성을 보존하는 역할을 합니다.
메모리 할당
- 할당이 필요한 경우:
*rest
또는**keyword rest
와 같이 명시적으로 객체를 요구하는 매개변수나 특정 C 메서드 옵션의 경우 객체 할당이 필수적입니다. 피호출자 메서드의 본문이 인자를 영구적으로 유지할 수 있기 때문에 호출자는 항상 객체를 할당해야 합니다. - C Ruby의 현재 할당 전략: 현재 C Ruby는 대부분의 경우 인자를 위한 객체를 미리 할당합니다. 이는 동적인 Ruby 코드 로딩과 복잡한 런타임 분석의 어려움 때문입니다.
- 할당 회피의 복잡성: 런타임에 호출 쌍을 분석하여 특정 상황에서 할당을 피하는 것은 성능 향상에 기여할 수 있지만, 매우 정교한 JIT 컴파일러와 광범위한 분석이 필요하며, 분석 범위에 따라 결과가 달라지는 복잡성이 있습니다.