Schwerpunkte

Software-Entwicklung

Embedded-Linux-Systeme tracen – Teil 6

29. Juni 2021, 11:00 Uhr   |  Tobias Schlichtmeier

Embedded-Linux-Systeme tracen – Teil 6
© Roman Samborskyi | shutterstock.com

Unsere Artikel-Reihe beschäftigt sich mit dem Tracen von Linux-Systemen. Zum Beispiel wie mit verschiedenen Optionen die beste Leistung aus dem System herauszuholen ist. Im sechsten und letzten Teil zeigen wir, wie man Tracealyzer zum Evaluieren von Python-Algorithmen einsetzt.

In Teil 4 der Artikel-Reihe wurde beschrieben, wie sich Tracealyzer zum Evaluieren von Userspace-Applikationen in einem Linux-basierten System einsetzen lässt. In dem damaligen Beispiel ging es um die Emulation eines potenziellen Features. Es wurde die Leistungsfähigkeit mehrerer Implementierungs-Kandidaten gegenübergestellt und beurteilt. Außerdem wurden die nötigen Funktionsaufrufe in der C/C++-Applikation zum Triggern von Tracepoints vorgestellt, die als Marker entlang der Tracealyzer-Zeitachse dienen sollen. Ferner wurde beschrieben, wie Tracealyzer zu konfigurieren ist, um die Tracepoints in »Custom Intervals« zu verwandeln, die letztendlich die nötigen Timing-Informationen zum Evaluieren der verschiedenen Implementierungen liefern.

Im sechsten und letzten Beitrag zeigen wir, wie sich Tracealyzer zum schnellen und effizienten Evaluieren mehrerer Implementierungsvarianten eines in Python geschriebenen Algorithmus nutzen lässt. Infolge der zunehmenden Bestrebungen, das Machine Learning ans Edge zu verlagern, setzt sich Python beim Entwickeln von Embedded-Anwendungen immer mehr durch, sodass Entwickler inzwischen die meisten Machine-Learning-Frameworks mit Python implementieren.

Wie man Fibonacci-Zahlen (nicht) berechnet

Anhand eines einfachen Beispiels ist ein interessantes Phänomen herauszustellen, das bei der Applikationsentwicklung in Python auftritt. Außerdem wird an dem Beispiel deutlich, wie sich die Kombination aus LTTng und Tracealyzer von Percepio nutzen lässt, um die Leistungsfähigkeit zweier Implementierungen von ein und demselben Algorithmus auf effiziente Weise zu vergleichen.

Die Fibonacci-Folge soll nachfolgend mit zwei gängigen Verfahren implementiert werden, nämlich zunächst mit einem rekursiven Algorithmus und danach mit einem standardmäßigen iterativen Verfahren. Anschließend kommen LTTng und Tracealyzer ins Spiel, um die Leistung beider Varianten miteinander zu vergleichen.

Bevor man jedoch an diesen Vergleich herangehen kann, ist zu gewährleisten, dass das Python LTTng-Softwarepaket installiert ist. Einzelheiten über die Installation des erforderlichen Softwarepakets finden sich auf der LTTng-Website.

Zunächst seien beide Implementierungen der Fibonacci-Folge vorgestellt, die hier in einem Modul zusammengefasst sind:

def recur_fibo(n):

  if n <=1 n:

    return n

  else:

    return(recur_fibo(n-1) + recur_fibo(n-2))

def non_recur_fibo(n):

  result = []

  a,b = 0,1

  while a < n:

    result.append(a)

    a,b = b, a+b

  return result

Hinzu kommt eine separate Python-Quelldatei, die die beiden Funktionen aufruft. Besonders hingewiesen sei auf die fett wiedergegebenen Zeilen, auf die weiter unten noch eingegangen wird.

import lttngust

import logging

import fib

def example():

  logging.basicConfig()

  logger = logging.getLogger(‘my-logger’)

  logger.info(‘Start’)

  fib.recur_fibo(10)

  logger.info(‘Stop’)

  logger.info(‘Start’)

  fib.non_recur_fibo(10)

  logger.info(‘Stop’)

if __name__ == ‘__main__’:

  example()

Anschließend wird mit den folgenden Anweisungen ein Trace für das Verarbeiten mit Tracealyzer erfasst:

$> lttng create

$> lttng enable-event --kernel sched_switch

$> lttng enable-event --python my-logger

$> lttng start

$> python3 <example source file>.py

$> lttng stop

$> lttng destroy

Im obigen Ausschnitt kommt es auf die fett dargestellte Anweisung an. Hier wird der normale Python-Logger durch einen solchen mit der Bezeichnung »my-logger« ersetzt und die entsprechenden Ereignisse werden im resultierenden LTTng-Trace abgelegt. Fett wiedergegebene Zeilen im davor gezeigten Python-Programmausschnitt richten den als »my-logger« bezeichneten Logger ein und geben die Ereignisse in der Umgebung der zu testenden Funktionen aus. Der tatsächliche Severity-Level der Logs kann beliebig sein, da er ignoriert wird. Wie man sieht, haben die Mechanismen, die zur Ausgabe von Ereignissen zu verwenden sind und hiermit das Markieren von Funktionsgrenzen ermöglichen, Ähnlichkeit mit jenen, die bereits in früheren Beiträgen der Artikel-Reihe angewendet wurden.

Timeline Trace
© Percepio

Bild 1. In der Trace-Ansicht von Tracealyzer sind die mit »my-logger« erzeugten Ereignisse erkennbar.

Ist der Trace erstellt, lässt er sich in Tracealyzer öffnen. Wie man sieht, erscheinen die generierten Ereignisse in der Trace-Ansicht (Bild 1). Da in dem speziellen Beispiel keinerlei Daten erfasst werden, sind ebenso keine Ereignis-Interpretationen zum Lesen der Datenwerte zu konfigurieren. Nötig ist lediglich das Erstellen eines Custom Intervals, um die Ein- und Aussprungpunkte dieser Funktionen zu markieren.

In Bild 1 ist zwar bereits eindeutig erkennbar, dass zwischen beiden Implementierungen eine erhebliche Performance-Differenz besteht, dennoch sind etwas greifbarere Performance-Werte zu extrahieren. Wie in einem früheren Beitrag bereits erläutert, kann das in Tracealyzer über den Menüpunkt »Views« und anschließendes Anklicken der Menüpunkte »Intervals« und »State Machines« erfolgen. Anschließend klickt man auf »Custom Intervals« und erstellt das in Bild 2 gezeigte individuelle Intervall.

Parameter
© Percepio

Bild 2. In diesem Fenster können die Parameter für das zu erstellende Intervall definiert werden.

Da die Strings »Start« und »Stop« zum Markieren der Ein- und Aussprungpunkte der zu vergleichenden Funktionen gewählt wurden, sollen die Strings ebenso benutzt werden, um den Beginn und das Ende des Custom Intervals zu kennzeichnen. Nach einem Klick auf die Schaltfläche »Save« wird die Trace-Ansicht aktualisiert. Wie in Bild 3 zu sehen ist, erscheint jetzt ganz rechts ein neuer roter Balken, der das soeben definierte Intervall wiedergibt.

Timeline Interval
© Percepio

Bild 3. Das zuvor definierte Custom Interval erscheint jetzt als roter Balken ganz rechts.

Als nächstes öffnet man die Ansicht Interval Plot per Klick auf »Views« und anschließendes Anklicken von »Interval Plot - Select Set…«, und passt das resultierende Diagramm so an, dass keine Linien angezeigt werden (Bild 4). Es ist zu erkennen, dass zwischen dem zuerst ausgeführten rekursiven Algorithmus und dem anschließenden iterativen Algorithmus ein zwanzigfacher Performance-Unterschied besteht.

Hiermit wird umgehend deutlich, dass Rekursionen in Python prinzipbedingt langsam sind. Wie man im Python-Listing sehen kann, wurde jeder Algorithmus nur zehnmal ausgeführt. Ohne Tracealyzer hätte man dagegen deutlich mehr Iterationen vornehmen müssen, um aussagefähige Erkenntnisse zu gewinnen. Dennoch ist die Vorgehensweise problematisch, und zwar aus zwei Gründen. Zunächst einmal bleibt Python einfach hängen, wenn man den rekursiven Algorithmus mit 1.000 oder lediglich 100 Iterationen ausführt – man kann das leicht selbst ausprobieren. Für einen Entwickler wäre das jedoch ein echtes Problem, da man nicht wissen kann, ob die enorm schleppende Verarbeitung auf einen Fehler in der Implementierung oder auf etwas völlig anderes zurückzuführen ist.

Performance-Differenz
© Percepio

Bild 4. Das Diagramm verdeutlicht die zwanzigfache Performance-Differenz zwischen dem rekursiven und dem iterativen Algorithmus.

Die Frage wird umso relevanter, je komplexer der Algorithmus oder die Implementierung wird, denn man müsste hier ein wesentlich umfangreicheres Logging ins Spiel bringen, um die Engstelle zu lokalisieren. Nun zum zweiten Punkt: Laufen im betreffenden Embedded-System mehrere Anwendungen, könnten die anderen Anwendungen die eigene unterbrechen und so dafür sorgen, dass das Verarbeiten des Algorithmus oder der Funktion noch mehr Zeit beansprucht. Ohne Trace könnte man nicht feststellen, ob das der Fall ist.

Tracepoints erfordern lediglich minimalen Aufwand

Mit der Kombination aus LTTng in Python und Tracealyzer lässt sich ein grundlegendes Wesensmerkmal von Python aufdecken, was beim Entwickeln komplexerer Algorithmen von unschätzbarem Wert ist. Da Tracepoints lediglich einen minimalen Aufwand verursachen, können sie in der Applikation belassen werden, wenn der Entwickler sie auf dem Zielsystem testet. Hiermit lassen sich Aussagen darüber gewinnen, wie leistungsfähig die Applikation an sich ist, ebenfalls unter Berücksichtigung anderer aktiver Anwendungen.

Ferner dient die soeben beschriebene Implementierung als Referenz dafür, wie sich die Performance künftiger Algorithmen-Implementierungen evaluieren lässt. Zentrale Funktionen wurden wie beschrieben in ein separates Python-Modul ausgelagert. Das ist nicht nur grundsätzlich eine gute Programmierpraktik, sondern erlaubt es ebenfalls, die Performance bestimmter Funktionen gezielt in den Blick zu nehmen. Beim Erstellen eines umfassenderen Python-Moduls, von dem aus die zentralen Funktionen direkt aufzurufen sind, richtet man dann eine Reihe von »Test«-Modulen ein, die ähnliche Ereignisse ausgeben, bevor die Aufrufe tatsächlich an die zu prüfenden Funktionen gerichtet werden.

Weil der für das Tracing entstehende Mehraufwand nahezu vernachlässigbar ist, sind ebenfalls Performance-Werte der Produktions-Codebasis zu extrahieren. Das wäre von größtem Wert für die regulären Systemtests, für die – mit minimalen Änderungen – dieselbe Codebasis herangezogen werden könnte. So ist zu gewährleisten, dass die Applikation funktional korrekt und leistungsfähig ist.

Einfach Performance-Werte extrahieren

Es wurde demonstriert, wie sich Tracealyzer und LTTng nutzen lassen, um die Performance-Werte einer Python-Applikation zu extrahieren. Da das nur einen minimalen Mehraufwand bedeutet, kann die Instrumentierung im Zielsystem verbleiben, um die Interaktionen der eigenen Applikation nicht nur mit anderen Anwendungen, sondern genauso mit dem Betriebssystem zu überwachen und zu verstehen. Zum Beispiel ist es denkbar, dass ein anderer Prozess oder Thread die eigene Applikation unterbricht und ihre Leistung somit beeinträchtigt. Mit Tracealyzer und LTTng lassen sich die Ursachen solcher Performance-Anomalien verstehen, um anschließend einen Feinschliff an der Implementierung vorzunehmen, sodass solche Phänomene vermieden werden.

Mit der Wahl eines eher unspektakulären Beispiels wurde eine zentrale Eigenschaft von Python deutlich, die sich in künftigeren, komplexen Implementierungen nutzen lässt. Gezeigt wurde außerdem ein geeignetes Design zum Messen und Validieren der Leistungsfähigkeit grundlegender Funktionen, die weitgehend isoliert gehalten werden können. Schließlich wurde demonstriert, wie sich der Mechanismus ausweiten lässt, um mit minimalen Modifikationen an der Umgebung und am Setup zu gewährleisten, dass die Anwendung funktional korrekt und performant ist.

Das ist der letzte Teil der Artikel-Reihe über das Tracen von Embedded-Linux-Systemen. In Teil 1 erfahren Sie, wie man den Treiber zum optimalen Nutzen von Tracealyzer für Linux vorbereitet. Im zweiten Beitrag zeigten wir, wie Tracealyzer die Leistungsfähigkeit eines IRQ Handlers gewährleistet. Teil 3 beschäftigt sich mit der maximalen Leistung einer Linux-Anwendung und deren Messung. Auch in Teil 4 ging es um die Leistung, genauer um das Evaluieren der Userspace-Performance. Der fünfte Beitrag rundet den Leistungs-Teil mit Compiler-Optionen für die maximale Performance ab.

Mohammed Billoo
© Percepio

Mohammed Billoo ist Gründer von MAB Labs, LLC, einem Anbieter kundenspezifischer Embedded-Linux-Anwendungen.

Der Autor

Mohammed Billoo ist Gründer von MAB Labs, einem Anbieter kundenspezifischer Embedded-Linux-Anwendungen für eine Vielzahl von Hardware-Plattformen. Billoo verfügt über einen Master in Elektrotechnik und mehr als 12 Jahre Erfahrung in Design, Implementierung und Test von Embedded-Software. Er trägt aktiv zum Linux-Kernel sowie zahlreichen Open-Source-Bemühungen bei und unterrichtet zudem als außerordentlicher Professor für Elektrotechnik an der Cooper Union for the Advancement of Science and Art.

Auf Facebook teilen Auf Twitter teilen Auf Linkedin teilen Via Mail teilen

Das könnte Sie auch interessieren

Verwandte Artikel

Percepio AB