지속성(Continuation)은 현재 평가하려는 식의 값을 받은 후 이어지는 모든 계산을 형식적으로 정의한 것으로, 런타임에서의 실행 지점이라고 볼 수 있습니다. 루비는 callcc
(call with current continuation)를 통해 이러한 지속성을 일급 객체로 다룰 수 있도록 지원합니다. callcc
는 setjmp
/longjmp
와 유사하게 동작하며, 한 번 캡처된 지속성을 여러 번 호출할 수 있는 다중 호출(multi-shot) 특성을 가집니다. 비록 Ruby 2.2.2부터 callcc
가 비권장되고 코루틴 기능을 위한 Fiber 사용이 권장되지만, Fiber가 단일 호출(one-shot) 특성만을 가지므로 callcc
는 다중 호출이 필요한 비결정 계산(non-deterministic computation)을 위한 DSL 구현 등 특정 시나리오에서 여전히 유용합니다.
발표에서는 백트래킹(backtracking)을 수행하는 DSL 구현을 예시로 callcc
의 잠재력을 설명합니다. 하스켈의 리스트 모나드처럼 평이하게 백트래킹 코드를 작성할 수 있는 DSL을 루비에서 구현할 때, 단순히 flat_map
을 사용하는 방식은 콜백 지옥을 초래합니다. 메타 프로그래밍을 통한 DSL 구현은 구문 추가 시마다 DSL 구현을 수정해야 하고 변수 관리가 복잡해지는 단점이 있습니다. 반면, callcc
를 활용하면 루비의 일반적인 변수 할당 구문을 사용하면서도 내부적으로 백트래킹을 통해 변수 값을 변경하며 코드를 반복 실행할 수 있어, DSL의 유연성과 개발 편의성을 크게 향상시킵니다.
그러나 callcc
는 몇 가지 심각한 문제점을 안고 있습니다. 첫째, callcc
는 현재 실행 중인 모든 지속성을 캡처하므로, DSL 구현 시 실제 필요한 범위보다 과도한 컨텍스트(불필요한 이후 코드까지)를 포함하게 됩니다. 이로 인해 코드의 복잡성이 증가하며, 캡처된 지속성 호출 후 원래 실행 지점으로 명시적으로 돌아오는 처리가 필요해 구현이 까다로워집니다. 둘째, callcc
의 성능은 매우 비효율적입니다. ‘SEND+MORE=MONEY’ 퍼즐과 같은 문제 해결 시, 메타 프로그래밍 방식으로는 0.13초 만에 완료되는 반면, callcc
방식으로는 수분 이상 소요되거나 아예 완료되지 못하는 경우가 발생합니다. 이는 callcc
가 루비 VM의 스택과 실행 위치를 스냅샷으로 기록하고 복원하는 방식으로 구현되기 때문입니다. 스택의 memcopy
와 setjmp
/longjmp
를 통한 VM 상태 저장 및 복원 과정은 상당한 오버헤드를 발생시키며, 루비 내부적으로 일부 최적화(차등 백업)가 이루어지더라도 근본적인 성능 저하를 피하기 어렵습니다.
이러한 callcc
의 문제점을 해결하기 위한 대안으로 ‘제한된 지속성(delimited continuation)’ 연산자인 shift/reset
이 소개됩니다. shift/reset
은 reset
블록 내에서 shift
를 통해 필요한 범위의 지속성만을 캡처합니다. 이는 callcc
의 과도한 컨텍스트 캡처 문제를 해결하여 DSL 구현을 훨씬 간결하게 만듭니다. shift/reset
은 캡처된 지속성을 함수처럼 활용하여 flat_map
등에 전달할 수 있으며, reset
지점에 도달하면 자동으로 원래 흐름으로 복귀하여 callcc
와 달리 명시적인 복귀 처리가 필요 없습니다. 놀랍게도 shift/reset
은 callcc
를 사용하여 구현할 수 있으며, 이렇게 구현된 shift/reset
조차 callcc
를 직접 사용하는 것보다 월등히 나은 성능을 보여줍니다 (예: ‘SEND+MORE=MONEY’ 퍼즐이 0.5초 내에 완료). 이는 shift/reset
의 구조가 callcc
의 내부 스택 최적화가 더 효율적으로 작동하도록 프로그램을 정리하기 때문으로 추정됩니다.
발표자는 향후 과제로 shift/reset
을 루비 VM의 내부를 직접 다루는 C 확장 라이브러리로 구현하여 스택 저장 범위를 더욱 제한하는 최적화를 통해 성능을 극대화할 계획임을 밝혔습니다. 이를 통해 shift/reset
은 DSL 개발의 편의성과 실행 효율성을 동시에 만족시키는 강력한 도구가 될 잠재력을 가지고 있습니다.