2022년 Embassy Async Rust와 FreeRTOS/C 비교, HN에서는 최악 지연 측정과 워크로드 설계가 쟁점
- 글은 STM32F446ZET6 180MHz에서 Embassy/Rust와 FreeRTOS/C로 같은 동작을 구현하고 인터럽트 지연, 프로그램 크기, RAM 사용량, 구현 난이도를 비교하겠다고 설명함
- Rust의 async 함수는 상태 기계인 Future로 변환되며, executor는 Future를 poll하다가 Waker가 깨울 때 다시 실행함
- Embassy는 할당기 없이 동작하도록 task를 정적으로 할당하고 컴파일 시점에 모두 알게 하며,
type_alias_impl_trait때문에 nightly 컴파일러를 요구함 - Embassy의 비동기 GPIO 대기는 EXTI 인터럽트가 전역 waker 배열의 해당 task를 깨우고, 인터럽트가 끝난 뒤 executor가 task를 다시 poll하는 방식임
- Embassy는 await 지점에서만 task를 바꾸는 협력형 멀티태스킹을 사용하며, RTOS 스레드는 프로세서 문맥을 저장해 선점형 스케줄링과 우선순위 실행을 지원함
Hacker News opinions
이 비교는 CPU 부하가 거의 없을 때만 성립함. 하드 실시간이면 표준편차가 아니라 최악의 인터럽트 지연을 봐야 하고, run-to-completion 작업이 있으면 가장 긴 계산 작업이 이상치를 만듦.
좋아하는 언어가 더 낫다고 보이게 하려면 그 결론이 나오는 벤치마크를 고르게 됨.
Embassy는 좋아하고 글도 재미있지만, 실제 임베디드 소프트웨어의 모습은 이렇지 않음.
요즘 Embassy는 우선순위별 task pool과 executor를 둬서 선점할 수 있음. 지연에 민감한 작업이 백그라운드 계산 executor를 끊을 수 있음.
내 경험상 마이크로컨트롤러에서 실제 계산을 오래 돌리는 경우는 드묾. 백그라운드 부하가 있으면 RTIC의 작업 우선순위와 선점을 써서 낮은 우선순위로 계산을 돌리면 됨.
ISR에서 전부 처리하는 RTIC식도 방법임. Cortex NVIC처럼 vectored, nested, tail-chained 인터럽트를 잘 지원하는 칩에서는 가능하고 깔끔함.
오실로스코프 화면에서 이상치를 찾기보다 timestamped GPIO를 수집해서 실제 통계를 내는 편이 나음.
제목을 바꿔야 함. Async Rust와 RTOS는 동등한 개념이 아니고, RTOS는 보통 선점형 스레드인데 async는 yield 지점에서 협력적으로 전환됨.
협력형 멀티스레드 RTOS도 존재함. 고정된 yield 지점으로 스레딩하는 추상화는 async Rust만의 것이 아니라서 비교 자체는 가능하다고 봄.
웹에서 async/await가 스레드를 이겼다는 말은 좀 공허함. 웹은 원래 스레드를 지원하지 않았고 지금도 worker 사이에는 메시지 전달만 있음.
이 글은 이제 거의 5년 전 자료라 Embedded Rust 기준으로는 꽤 오래됨. 큰 흐름은 비슷해도 세부 사항을 그대로 믿기는 조심스러움.
요즘은 Zephyr가 사실상 Linux급 관성을 얻은 듯함. 거의 모든 벤더가 지원하고 Rust 지원은 일부에서만 제한적인데, AI 시대에 언어 차이가 얼마나 중요한지도 모르겠음.
언어는 여전히 중요함. C에서 튼튼한 코드를 쓰려면 여러 규율이 필요하고, Rust는 변경이 버그를 넣지 않는다는 확신을 더 줌.
생산자는 소비자보다 훨씬 빠른데 버튼 지연만 보는 테스트임. USART로 20바이트를 찍는 데도 약 1.7ms인데, 이 정도로 제한된 작업이면 협력형 FreeRTOS나 단일 이벤트 루프면 충분함.
C를 측정한다고 해놓고 예시 코드는 C++임.