Virtuelle Funktionen in Mikrocontrollern - die dunkle Seite

Ist die Situation bekannt, wenn der Platz auf dem Blitz ausgegangen ist und Sie das Ungenießbare schieben müssen, um das zu opfern, was Sie brauchen? Versuchen wir stattdessen, unnötige Opfer zu bringen, sie verstecken sich an eher unerwarteten Orten.

Ich wollte einen Telnet-Server für die Steuerung verschiedener Geräte auf dem beliebten und kostengünstigen WIZnet W5500-Modul erstellen. Alles, was dazu benötigt wird, ist Teil der Arduino-Standardbibliothek, das Ergebnis finden Sie hier . Aber es geht nicht um ihn. Das erste, was mich wirklich überraschte, war, dass dieser einfache Funktionscode mehr als die Hälfte des ATmega328P-Flashs in Anspruch nahm. Natürlich enthält die Ethernet-Bibliothek viel Code, aber nicht alles wird verwendet. Der Compiler muss nicht verwendeten Code aus der zusammengestellten Firmware entfernen. Überprüfen Sie, ob dies so ist.

Wir gehen in das Verzeichnis, in dem die Assemblierung stattfindet - der Pfad dazu ist in den Kompilierungsnachrichten zu sehen, und führen objdump -t <elf firmware file> aus, um die Zeichentabelle abzurufen. Wir sehen darin viele Ethernet-bezogene Funktionen, einschließlich solcher, deren Bedarf nicht offensichtlich ist, zum Beispiel Funktionen für die Arbeit mit UDP. Das heißt, es sieht so aus, als ob die Entfernung unnötiger Funktionen nicht stattgefunden hätte. Was ist los

Die Antwort mag unerwartet erscheinen - das Ganze ist die Vererbung von Klassen, die Ethernet von Basisklassen mit vielen virtuellen Funktionen implementieren. Der Compiler geht davon aus, dass eine Funktion (oder Klassenmethode) verwendet wird, wenn an anderen Stellen im Code Links zu dieser Funktion vorhanden sind. Um eine solche Verknüpfung zu erstellen, muss die Funktion jedoch nicht aufgerufen werden. Es reicht aus, ihre Adresse zu speichern. Auch wenn wir dies nicht explizit tun, erledigt C ++ dies für uns, indem es einen Zeiger auf eine Funktion in der virtuellen Funktionstabelle platziert. Auch wenn wir diese virtuelle Funktion niemals verwenden, ist sie in der Firmware enthalten. Wenn eine Funktion in der Basisklasse als rein virtuell (ohne Implementierung) definiert ist, haben wir keine anderen Optionen, als sie zu implementieren, auch wenn wir sie überhaupt nicht benötigen, wodurch der Firmware-Code vergrößert wird.

Wir überprüfen die Richtigkeit unserer Hypothese. Nehmen Sie zum Beispiel hier die Ethernet-Bibliothek aus dem Github, um den Standard nicht zu berühren, und ändern Sie ihn. Wir entfernen Vererbung und stellen virtuelle Funktionen einfach durch Methoden her. Wie Sie es vorsichtig und reversibel machen, erfahren Sie hier . Ergebnis: Die Codegröße wurde um 4460 Byte verringert - mehr als ein Viertel der ursprünglichen Größe.

Natürlich sind Vererbung und virtuelle Funktionen nützlich. Das Erstellen einer Basisklasse mit reinen virtuellen Funktionen zum Definieren einer Schnittstelle für nachfolgende Implementierungen ist jedoch nicht immer gerechtfertigt. Zunächst müssen Sie sicherstellen, dass Sie diese Schnittstelle wirklich mit Objekten unterschiedlichen Typs verwenden. Andernfalls ist die in der Basisklasse (z. B. in der Print-Klasse) implementierte Funktionalität für Sie hilfreich.

Source: https://habr.com/ru/post/de483862/


All Articles