Im ersten Beitrag zu diesem Thema haben Sie erfahren, wie Sie RTOSBench, eine Benchmark-Suite für Kernel-Dienste, auf FreeRTOS und Zephyr portieren können. Im nächsten Schritt werden wir die Tests von RTOSBench auf beiden Betriebssystemen ausführen und die Ergebnisse analysieren.
Alle Tests wurden auf einem STM32F429ZIT6 durchgeführt, wobei jedes Testszenario eintausend Mal ausgeführt wurde. Dieser Ansatz ermöglicht es uns, die Konsistenz der Ausführungszeit zu verstehen.
Kernel-Konfiguration
Bevor wir die Testergebnisse analysieren, schauen wir uns zunächst die Konfiguration der beiden Betriebssysteme an. Für beide Beiträge arbeiteten wir mit der zu dem Zeitpunkt neuesten Version, also FreeRTOS v10.4.4 und Zephyr v2.6.0. Wir mussten sicherstellen, dass die zu testenden Betriebssysteme möglichst ähnlich konfiguriert sind, damit wir vergleichbare Ergebnisse erhalten. Im Folgenden sehen Sie Auszüge aus den relevanten Kernel-Konfigurationsparametern, die ich verwendet habe:
FreeRTOS-Konfiguration
/* Tickrate reduzieren, um Timer-Interrupts während der Benchmarks zu vermeiden */
#define configTICK_RATE_HZ ((TickType_t)20)
/* Asserts vollständig deaktivieren */
#define configASSERT( x )
/* Erforderliche Datenstrukturen auswählen */
#define configUSE_MUTEXES 1
#define configUSE_RECURSIVE_MUTEXES 1
#define configUSE_COUNTING_SEMAPHORES 1
Zephyr-Konfiguration
# Tickrate reduzieren, um Timer-Interrupts während der Benchmarks zu vermeiden
CONFIG_SYS_CLOCK_TICKS_PER_SEC=20
CONFIG_TICKLESS_KERNEL=n
# Idle Power Management deaktivieren
CONFIG_PM=n
# Speicher/Code-Footprint reduzieren
CONFIG_BT=n
# Asserts vollständig deaktivieren
CONFIG_FORCE_NO_ASSERT=y
# Stack Protection ist ab Zephyr v2.5.0 standardmäßig aktiviert. Dies ist ein erheblicher
# Overhead während Benchmarks. FreeRTOS aktiviert Stack Protection nicht
# standardmäßig. Daher schalten wir ihn auch in Zephyr aus, um sauberere Ergebnisse zu erhalten.
CONFIG_HW_STACK_PROTECTION=n
CONFIG_COVERAGE=n
# Timing-Subsystem aktivieren
CONFIG_TIMING_FUNCTIONS=y
# RTOSBench erlauben, Interrupts während der Laufzeit zuzuweisen
CONFIG_DYNAMIC_INTERRUPTS=y
Analyse der Ergebnisse
Der Einfachheit halber haben wir nur eine Teilmenge der RTOSBench-Tests durchgeführt. Das Balkendiagramm unten zeigt die durchschnittliche Anzahl der Zyklen für jeden Test.
Durchschnittliche Anzahl der Zyklen für jeden ausgeführten RTOSBench-Testfall. Jeder Test wurde eintausend Mal wiederholt.
Kontextwechsel
Der erste Test („Kontextwechsel“) misst die Zeit zwischen der freiwilligen Abgabe der CPU durch eine kooperative Aufgabe und der Ausführung der nächsten Aufgabe. Hier benötigt Zephyr mehr als doppelt so viele Zyklen (524) wie FreeRTOS (223). Dies ist ein wesentlicher Unterschied zwischen den beiden Betriebssystemen. Daher ist es wichtig, die für Zephyr gemessene Ausführungszeit zu überprüfen.
Glücklicherweise bietet die Zephyr-eigene Testsuite einen Benchmark (zu finden unter tests/benchmarks/latency_measure im Zephyr-Quellbaum), der auch kooperative Kontextwechsel abdeckt.
Wir können unser Ergebnis verifizieren, indem wir es mit dem aus der Zephyr-Testsuite vergleichen. Die Ausführung des Zephyr-Benchmarks ergab eine durchschnittliche kooperative Kontextwechsel-Latenz von 468 Zyklen. Die Differenz von 56 Zyklen könnte auf den von RTOSBench erzeugten Overhead zurückzuführen sein. Dass die Ergebnisse relativ nahe beieinander liegen, deutet darauf hin, dass unser ursprüngliches Ergebnis valide ist.
Interrupt-Latenz
Der zweite Benchmark („Interrupt-Latenz“) misst die Zeit, die vom Auslösen eines Interrupts auf dem Interrupt-Controller des Boards bis zur Ausführung der entsprechenden Interrupt-Service-Routine vergeht. Auch hier schneidet FreeRTOS mit nur 101 Zyklen im Vergleich zu Zephyr mit 143 Zyklen im Durchschnitt besser ab.
Inter-Task-Synchronisation
Das nächste Testszenario besteht aus zwei Teilen und testet die Leistung eines bekannten Synchronisationsmechanismus: Mutex. Hier zeigen die Balken mit der Bezeichnung „Mutex Block“ die Latenzzeit beim Blockieren eines Tasks, wenn ein anderer Task bereits den Mutex hält. Für diesen Vorgang benötigt Zephyr nur 969 Zyklen, während FreeRTOS 1.964 benötigt.
Hier eine mögliche Erklärung für den signifikanten Unterschied in der Ausführungszeit: FreeRTOS verwendet seinen Code für Message Queues erneut, um Mutexe zu implementieren. Zephyr verwendet eine gesonderte Mutex-Implementierung. Dadurch kann Zephyr seine Implementierung optimieren, wenn auch mit einer größeren Codebasis. „Mutex Unblock“ gibt die Zeit an, die zwischen der Freigabe eines Mutex und der Ausführung eines auf diesen Mutex wartenden Tasks vergeht. Dieser Vorgang ist in FreeRTOS mit durchschnittlich 1.212 Zyklen gegenüber 1309 Zyklen in Zephyr etwas schneller.
Inter-Task-Kommunikation
Die Ergebnisse der Message Queuing Benchmarks ähneln denen des Mutex-Testfalls. Die Balken mit der Bezeichnung „Message Queue Block“ zeigen die durchschnittliche Anzahl von Zyklen, die ein Task benötigt, um eine leere Message Queue zu blockieren, einschließlich der Zeit für den Kontextwechsel zum nächsten Task. Für diesen Vorgang benötigt Zephyr 710 Zyklen, FreeRTOS hingegen 1.660 Zyklen. „Message Queue Unblock“ gibt die Latenzzeit zwischen dem Senden einer Nachricht an die Message Queue und dem Aufwecken eines (höher priorisierten) Tasks an, der auf eine Nachricht in derselben Queue wartet. Mit 923 Zyklen erledigt Zephyr diesen Task etwas schneller als FreeRTOS, das 1.058 Zyklen benötigt.
Fazit
Als Beispielbetriebssysteme wurden Zephyr und FreeRTOS verwendet. Die hier vorgestellten Ergebnisse sollten jedoch nicht als Beweis dafür angesehen werden, dass ein Betriebssystem besser ist als das andere. Stattdessen können sie als Anhaltspunkt dafür dienen, dass ein Betriebssystem für einen bestimmten Anwendungsfall besser geeignet ist. So könnte sich FreeRTOS beispielsweise besser für Anwendungen mit vielen Kontextwechseln eignen, Zephyr hingegen für Anwendungen mit viel Inter-Task-Kommunikation.
Außerdem bieten beide Betriebssysteme viele Konfigurationsparameter, die die Benchmark-Ergebnisse beeinflussen können, wenn eine Anpassung an die jeweilige Anwendung erfolgt. Generell sollten Sie den Kernel an die Anforderungen Ihrer Anwendung anpassen, bevor Sie diese Benchmarks durchführen.
Bei näherer Betrachtung der Ergebnisse fällt auf, dass Blockiervorgänge unter FreeRTOS deutlich länger dauern als unter Zephyr. Ein möglicher nächster Schritt zur weiteren Untersuchung wäre die Nutzung der Tracing-Funktionen von FreeRTOS, um die Vorgänge genauer zu untersuchen.