MicroRuby와 PicoRuby의 차이 및 메모리 소비 요인
- PicoRuby: PicoRuby 컴파일러와 mrbc(mruby/c) 가상 머신의 조합입니다.
- MicroRuby: PicoRuby 컴파일러와 mruby 가상 머신의 조합입니다.
초기 ‘Hello World’ 애플리케이션에서는 mrbc가 mruby보다 훨씬 적은 메모리를 소비하는 것으로 나타났습니다. 그러나 실제 IoT 애플리케이션과 같은 대규모 환경에서는 mruby의 다음과 같은 기능들이 메모리 효율성을 높일 수 있습니다.
- IREP (Intermediate Representation): 컴파일러가 VM용으로 변환한 코드가 메모리에 전개된 객체로, 각 코드 블록(클래스, 모듈, 메서드 등)을 나타냅니다.
- Pre-sym (Precompiled Symbols): mruby 3.0에 도입된 기능으로, 심볼 테이블과 IREP를 RAM이 아닌 ROM에 미리 전개하여 바이너리에 포함시키는 빌드상의 최적화입니다. 이는 mrbc나 CRuby에는 없는 mruby 고유의 기능입니다.
- Object Space: mruby는 루비 객체를 하나의 할당 영역에 연속적으로 배치하여 메타데이터 오버헤드를 줄입니다. 반면 mrbc는 객체마다 개별 할당을 수행하여 메타데이터 및 패딩으로 인한 비효율성이 발생할 수 있습니다.
하미킨의 예상 (Hamikin’s Conjecture) 및 검증
이러한 mruby의 특성들을 바탕으로 ‘하미킨의 예상’이 제시되었습니다. 이는 소규모 애플리케이션에서는 PicoRuby가 유리하지만, 애플리케이션 규모가 커질수록 MicroRuby의 메모리 소비 증가율이 완만해져 어느 시점에서 PicoRuby와 교차하는 ‘건배 지점(Kampai Point)’이 발생할 것이라는 가설입니다.
이 가설을 검증하기 위해 PicoRuby용으로 개발된 R2P2(쉘과 유사한 대규모 애플리케이션)를 MicroRuby에 포팅하는 작업이 진행되었습니다.
구현 세부 사항
- 선점형 태스크 스케줄링: mruby에는 없는 기능으로, R2P2와 같은 OS 유사 애플리케이션 구동에 필수적입니다. C Ruby의 스레드와 유사하게, 인터럽트를 통해 CPU 리소스를 여러 태스크에 할당하는 방식입니다.
mrb_context
구조체를 활용하여 태스크를 구현하고, 10ms마다 태스크를 전환하는 메커니즘을 도입했습니다. - GC (Garbage Collection) 처리: 여러
mrb_context
인스턴스가 존재할 때, GC의 루트 스캔 단계에서 모든 컨텍스트를 최상위 레벨로 마크하여 불필요한 객체 회수를 방지하도록 GC 로직을 수정했습니다. - 커스텀 메모리 할당기 (est_alloc): 마이크로컨트롤러 환경에서는 기존
newlib
의malloc
이 메모리 사용량 보고 기능을 제공하지 않으므로, mrbc의 내장 할당기를 기반으로est_alloc
이라는 커스텀 할당기를 개발하여 정확한 메모리 측정을 가능하게 했습니다.
메모리 측정 결과 및 결론
R2P2 애플리케이션을 MicroRuby에 포팅하여 측정한 결과, Wi-Fi 연결 상태에서 MicroRuby는 약 109KB의 RAM을 사용했습니다. 이와 비교하여 PicoRuby는 초기 49KB에서 R2P2 관련 Gem을 수동으로 로드했을 때 78KB를 사용했습니다. 약 30KB(30%)의 차이로 메모리 소비량 격차가 줄어들었으며, 이는 ‘하미킨의 예상’이 실제와 부합할 가능성을 시사합니다. Raspberry Pi Pico W(264KB RAM)에서는 다소 타이트할 수 있으나, Pico 2W(520KB RAM)에서는 충분히 실용적인 범위에 있음을 확인했습니다.