범용 VM이라는 내기 — Parrot은 왜 누구의 것도 되지 않았는가

Parrot은 「동적 언어 공통의 VM」을 목표로 했다. Perl 6도 Python도 Ruby도 Tcl도 올라가는 기반을 만든다는 내기다. 전제는 성립하지 않았다. 진지한 이용자가 Perl 6뿐이었기에, 범용성을 위해 치른 비용을 회수할 수 없게 되었다. 2013년, Rakudo는 MoarVM으로 옮긴다. 범용성을 버리고 전용으로 돌린 것으로, 비로소 완성했다.

perlrakuparrotmoarvmvmlanguage-implementationprogramming-languages

지난번, Pugs가 「돌아가는 것이 있다」는 것의 가치를 증명한 이야기를 썼다. 이번에는 그 이면 — Perl 6이 구현 기반의 선정에서 한 번 내기를 빗맞힌 이야기다.

이름의 유래는 만우절

2001년 4월 1일, 이런 농담 기사가 공개되었다.

Larry Wall과 Guido van Rossum이 Perl과 Python을 통합해, Parrot이라는 새 언어를 만든다.

Monty Python의 앵무새 스케치에 건 농담이다.

훗날 Perl 6의 VM이 실제로 만들어지게 되었을 때, 이 이름이 채용되었다. 이 유래 자체가, Parrot의 목표를 잘 나타내고 있다. 하나의 VM으로 여러 동적 언어를 돌린다는 발상이다.

설계상의 내기

Parrot은 Perl 6 전용 VM이 아니었다. 동적 언어 일반을 위한 VM을 목표로 했다. 상정하고 있던 대상은 Perl 6, Perl 5, Python, Ruby, Tcl, PHP, Scheme 등.

주요한 설계 판단은 이렇다.

판단 이유
레지스터 기계(스택 기계가 아니다) 동적 언어의 최적화에 유리하다는 읽기. JVM / CPython은 스택 기계
PMC(Polymorphic Container) 언어마다 다른 값의 의미론을, 공통의 그릇으로 다룬다
계속(continuation)을 일급으로 가진다 코루틴 · 예외 · 동적 스코프를 통일적으로 다룬다
풍부한 내장 타입 각 언어의 구현자가 VM 쪽 부품을 돌려쓸 수 있도록

주도한 것은 Dan Sugalski(수석 아키텍트). 훗날 Allison Randal 등이 이어받아, 2008년에 Parrot Foundation이 설립되었다. Parrot 1.0은 2009년 3월에 릴리스되었다.

이것은 진지하고, 그리고 지적으로 매력적인 설계였다. 뒷북으로 비웃을 것이 아니다. 동적 언어의 구현에 공통되는 일은 분명히 있고, 그것을 한 곳에 모은다는 발상은 올바른 형태를 하고 있다.

무엇이 잘 되지 않았는가

1. 진지한 이용자가 Perl 6뿐이었다

「여러 언어의 공통 기반」이라는 전제가 성립하지 않았다.

Python도 Ruby도, 이미 자체 구현이 있었고 각자 독자적으로 개량을 진행하고 있었다. Parrot으로 갈아탈 동기가 없다. 기존 C 확장도, 기존 성능 특성도, 기존 커뮤니티도 있다.

결과적으로, 범용성을 위해 치른 비용에 대해, 범용성의 대가가 없는 상태가 되었다.

Perl 6 전용으로 만들었다면 하지 않아도 되었을 추상화를, 계속 짊어지게 된다. PMC의 간접층도, 언어 중립적인 호출 규약도, 「다른 언어가 왔을 때를 위해」 존재하고 있었고, 그 다른 언어는 오지 않았다.

여기에, 이 연재에서 여섯 번째의 「형(型)」이 있다.

첫 번째 이용자로는, 범용인 척을 간파할 수 없다.

두 번째가 올 때까지, 그 추상화가 정말로 범용인지는 검증되지 않는다. Parrot은 두 번째가 오지 않았기에, 범용성이 검증되지 않은 채 무거워졌다.

까다로운 것은, 이 상태가 실패로 보이기 어렵다는 점이다. Parrot은 돌아가고 있었다. Rakudo는 Parrot 위에서 돌아가고 있었다. 「범용성을 회수하지 못하고 있다」는, 테스트가 빨개지는 종류의 문제가 아니다.

2. 성능이 나오지 않았다

레지스터 기계라는 내기는, 기대한 만큼의 차이를 낳지 않았다. GC, 호출 규약, PMC의 디스패치 — 각 층의 오버헤드가 쌓여, Rakudo on Parrot은 실용에는 느리다는 평가가 오래 이어졌다.

「Perl 6은 느리다」는 평판이 정착한 것은, 주로 이 시기다. 그리고 그 평판은, 기반이 갈아 끼워진 후에도 오래 남았다.

3. 움직이는 사양에 계속 맞췄다

Perl 6의 사양이 굳어지지 않은 시기에 VM을 만들고 있었기 때문에, 위의 요구가 바뀔 때마다 아래를 다시 만드는 왕복이 발생했다.

지난번에 쓴 「구현이 사양을 움직인다」는 왕복(Pugs가 시작한 것)은 건전한 기구지만, 그 왕복이 VM 층까지 닿으면, 비용의 자릿수가 달라진다.

청산 — 2013년, MoarVM

2013년, Rakudo는 주력 백엔드를 MoarVM으로 옮긴다.

MoarVM(Metamodel On A Runtime)은, 주로 Jonathan Worthington이 주도한 C제 VM이다.

Parrot의 교훈이, 설계 방침에 그대로 드러나 있다.

Parrot MoarVM
여러 동적 언어를 위한 범용 VM NQP / Rakudo 전용으로 단정
언어 비의존적인 추상을 가진다 Raku의 오브젝트 모델(6model)을 VM이 직접 안다
범용성의 비용을 항상 치른다 Raku가 필요한 것만 최적화한다

「범용 VM을 만들어 Perl 6을 올린다」에서 「Perl 6을 위한 VM을 만든다」로의 전환.

이것이, Perl 6이 완성으로 향한 기술적인 전환점이다. 2년 반 후인 2015년 12월에 6.c가 나오는 것은, 이 판단이 있었기 때문이다.

전용으로 돌렸기에 넣을 수 있었던 것

MoarVM의 특징을 보면, 「전용이기에 가능했던」 것들이 늘어선다.

NFG 문자열 — MoarVM은 문자열을 그래핌 단위로 가진다.

my $s = "a\c[COMBINING ACUTE ACCENT]";  # a + 결합 악센트
say $s.chars;   # 1

「보이는 한 글자」가 .chars의 1이 된다. 다른 언어와 늘어놓으면, Raku의 위치가 분명해진다.

언어 문자열의 단위
C 바이트
Java / JavaScript / C# UTF-16 코드 유닛
Python 3 / Go(rune) 코드포인트
Ruby 코드포인트(인코딩 포함)
Raku 그래핌

언어 중립적인 VM에서는, 이 선택을 할 수 없다. 「문자열이란 무엇인가」에 대해 특정한 입장을 취하게 되기 때문이다. 전용 VM이기에 넣을 수 있었다.

그 밖에도, 6model을 네이티브로 다루는 것, spesh(런타임 타입 정보에 의한 특수화), JIT, 정밀 GC, libuv에 의한 비동기 I/O — 어느 것이나 「Raku가 필요로 하는 것」에 조준이 맞춰져 있다.

Parrot을 어떻게 자리매김할 것인가

「실패」라고 한마디로 쓰는 것은 정확하지 않다. 실제로 남은 것이 있다.

  • Perl 6이 실제로 돌아가는 최초의 길을 만들었다 — Rakudo는 Parrot 위에서 태어났다
  • 중간 표현의 설계 경험이, NQP의 설계에 살아 있다
  • 여러 백엔드를 가진다는 발상이, NQP의 백엔드 중립 설계로 살아남았다

정확한 요약은 이렇다.

내기의 전제(여러 언어가 올라탄다)가 빗나갔고, 그것을 위해 치르고 있던 비용을 회수할 수 없게 되었다.

백엔드는 몇 개까지 유지할 수 있는가

그리고, Parrot의 교훈을 이어받았을 NQP에도, 같은 문제의 그림자가 있다.

NQP는 Raku의 서브셋으로, 여러 백엔드를 가지는 설계다. 새로운 VM에 대응하고 싶을 때, NQP만 이식하면 Rakudo가 따라온다. 이치로서는 아름답다.

실제는 이렇게 되어 있다(2026년 9월 시점).

백엔드 상황
MoarVM 사실상 유일한 실용 백엔드. 기본값
JVM 돌아가지만 기능 · 성능 모두 추종이 뒤처져 있다
JavaScript 실험적. 실용 수준에 도달하지 않았다

이유는 기술이 아니라 인원이다. 백엔드를 하나 유지하는 비용은 높다. 여럿을 같은 수준으로 유지하려면, 각각에 지속적인 담당자가 필요하다. Raku의 규모에서는 그것이 성립하지 않았다.

여기서, 일곱 번째의 형이 나온다.

백엔드가 N개 있다는 것과, N개가 검증되어 있다는 것은 다르다.

두 번째에 진지한 이용자가 없는 한, 중립적으로 썼다고 생각한 층은, 어느새 첫 번째의 사정에 맞춰져 있다.

여기는 남의 일이 아니다

나는 스스로 언어를 만들고 있고, 백엔드를 5개 가지고 있다 (인터프리터 · C · LLVM IR · Wasm · RISC-V 기계어).

Parrot과 NQP의 이야기를 조사하면서, 내가 하고 있는 일에 이름이 붙은 감각이 있었다. 5개 있다는 것은, 5개가 검증되어 있다는 것이 아니다.

내 경우, 그것을 일단 막고 있는 것은 인터프리터를 오라클로 삼아, 다른 3개의 출력을 바이트 단위로 대조하는 장치를 가지고 있다는 점이다 (5번째인 RISC-V는 QEMU 아래에서 기동시켜 확인한다). 「중립적으로 썼다고 생각한 것」을, 실제로 돌려서 비교한다.

그러나 이 장치에도 구멍이 있다. 오라클 자신이 틀렸다면, 5개가 함께 틀린다. Parrot이 「범용성을 회수하지 못하고 있다」는 것을 테스트로 검출할 수 없었던 것과, 종류로서는 같은 문제다.

그래서 이 연재의 다음 회의 주제는, 나에게도 절실하다. 사양이란 무엇인가. 무엇을 근거로 「옳다」고 말하는가.


다음 회(7화): 사양을 테스트 스위트로 정의한다. Perl 6은 산문 사양을 버리고, 「사양이란 roast를 통과하는 것이다」라는 형태로 옮겼다. 그 판단이 무엇을 가능하게 했고, 무엇을 놓았는가.

← Back to Perl과 Raku의 계보