· kaynak Hacker News – Front Page (native)
Tweede golf, async Rust Embassy'yi STM32F446 üzerinde FreeRTOS ile karşılaştırdı
Pratik bir karşılaştırma, aynı firmware'ı STM32F446 üzerinde async Rust (Embassy) ve C (FreeRTOS) olarak çalıştırıyor; interrupt gecikmesini, kod boyutunu, RAM kullanımını ve kullanım kolaylığını puanlıyor.

Tweede golf, aynı gömülü uygulamayı 180 MHz saat hızındaki bir STM32F446ZET6 üzerinde iki kez çalıştıran — bir kez FreeRTOS üzerinde C ile, bir kez async Rust ile Embassy kullanarak — ve her iki tarafı interrupt gecikmesi, program boyutu, RAM kullanımı ve programlama kolaylığı açısından değerlendiren pratik bir karşılaştırma yayımladı. Yazı Hacker News'in ana sayfasına ulaştı.
İki model nasıl çalışıyor
Yazıya göre Rust'ın async fonksiyonları state machine'e derleniyor: döndürülen future poll ediliyor, en son durakladığı yerden devam ediyor ve await noktaları arasında yaşayan değişkenleri kendi içinde tutuyor. Future'lar tembel — poll edilene kadar hiçbir şey çalışmıyor — ve executor'lar waker'lara güvenir, böylece bir future yalnızca bir şey ilerleyebileceğini bildirdiğinde yeniden poll edilir.
Embassy bunun üzerine, heap allocator eksikliğinden kaynaklanan kısıtlamalarla bu modeli genişletiyor: her görev statik olarak ayrılıyor, görevlerin tam kümesi derleme zamanında bilinmek zorunda ve type_alias_impl_trait özelliği için nightly derleyici gerekiyor. Peripheral sürücüleri async arayüzler sunuyor, böylece bir GPIO kenarı beklemek waker'ını global bir dizide kaydeden bir future'a dönüşüyor; donanım interrupt'u tetiklendiğinde, eşleşen girdi doğru görevi uyandırıyor ve executor onu yeniden poll ediyor. Özellikle, Embassy kooperatif: çalışan bir görev yalnızca bir şeyi await ettiğinde yerini bırakıyor.
Bir RTOS ters yaklaşımı benimsiyor. Thread'ler sıradan kod çalıştırıyor, yani mevcut herhangi bir fonksiyon thread olabilir, ancak her geçiş işlemcinin tam bağlamını kaydetmeli ve geri yüklemelidir. Bu tasarım pre-emption'ı doğal kılar: çekirdek öncelikleri uygular, thread'ler arasında CPU zamanını paylaşır ve olaylarla interrupt'lara öngörülebilir sınırlar içinde yanıt verebilir.
İş yükü
Gerçekçi kalıp sonsuz bir ince ayar uğraşına dönüşmemek için benchmark programı üç iş yapıyor. Bir LED, executor'ın veya RTOS'un delay primitive'ini kullanarak her 200 ms'nin 100 ms'si yanıyor. Bir buton görevi GPIO interrupt'u kuruyor, kullanıcı butonunun seviyesini paylaşımlı bir atomic boolean'da izliyor ve durum her değiştiğinde formatlanmış bir dizgiyi — buton seviyesi ve artan bir tetikleme sayacı — bir mesaj kuyruğuna itiyor. Üçüncü görev bu kuyruğu boşaltıyor ve seri porta yazdırıyor. Buton basılı tutulduğunda LED'in sönük kalması gerekiyor ve bu durumun pin register'ı doğrudan okunmak yerine görevler arası paylaşımlı değişkenle taşınması zorunlu kılınıyor; böylece testte gerçek görevler arası iletişim zorlanıyor.
Neler ölçülüyor
Zamanlama, GPIO pinleri problar olarak kullanılarak bir Rigol DS1054Z osiloskopuyla ölçülüyor. Bir pin buton interrupt'una girişte yükseliyor ve çıkışta düşüyor; böylece interrupt'un çalışma süresi elde ediliyor. İkinci bir pin, buton görevinin uyanması ile yeniden bloklanması arasında çalıştığı aralığı işaretliyor. İki yükselen kenar arasındaki boşluk, interrupt işleme gecikmesini veriyor — yani bir sinyalin donanım interrupt'undan çalışan görev koduna ulaşmasının ne kadar sürdüğünü.
Kapasite ölçümü arm-none-eabi-size ile yapılıyor: .text bölümü program boyutunu, .data artı .bss ise statik RAM'i temsil ediyor. Her görev ve thread statik olarak ayrılmış durumda; yazar, ölçülmesi zor olduğu için dinamik ve stack kullanımını bilinçli olarak dışarıda bırakıyor, ama statik olarak daha çok ayıran yazılımların genellikle daha az stack'e ihtiyaç duyduğunu belirtiyor. Programlama kolaylığı da puanlanıyor; yazar bu ölçütü açıkça öznel olarak niteliyor.
Temel kurallar
Karşılaştırma, gömülü kod ince ayarının neredeyse sonsuza kadar sürabileceği gerekçesiyle en yüksek performansın peşinden gitmeyi açıkça reddediyor. Her iki uygulama da aynı yönergelere uyuyor: HAL bağımlılıkları dışında diğer çiplere makul ölçüde taşınabilir, okunması kolay ve yalnızca derleyici optimizasyon seviyeleri, RTOS yapılandırması ve thread öncelikleri gibi sıradan ayarlarla ayarlanmış. Yazar Rust'a bir eğilimini kabul ediyor, testi geliştirmek için önerilere davet ediyor ve iki eşzamanlılık modelinin kendisine odaklanmak için tanıdık C'ye karşı Rust dil tartışmasını kapsam dışında bırakıyor.
Neden önemli
Pre-emptive bir RTOS ile kooperatif bir async executor arasında seçim yapmak, bir mikrodenetleyici projesindeki en büyük mimari kararlardan biridir ve soyut tartışmalar bunu nadiren çözer. Aynı çip, aynı iş yükü üzerinde bir benchmark, kooperatif zamanlamanın interrupt tabanlı kod için yeterince hızlı olup olmadığı gibi soruları söylenti yerine ölçülebilir veriye dönüştürüyor. Ölçüm tekniğinin kendisi — interrupt handler'ı ve uyandırılan görev çevresinde pinleri toggle'layıp darbeleri bir osiloskoptan okumak — hangi stack'i kullanırsa kullansın her gömülü geliştiricinin kendi gecikme kontrolleri için kopyalayabileceği bir numara. Yazının tamamı, bu metodolojiyi future'ların, waker'ların, executor'ların ve RTOS scheduler'larının gerçekte nasıl çalıştığının anlaşılır bir açıklamasıyla birleştiriyor; bu da onu FreeRTOS ile Embassy'yi tartan ekipler için yararlı bir arka plan okuması yapıyor.
- #rust
- #embedded
- #freertos
- #embassy
- #stm32
- #benchmark