Wie kann man Code für mehr Flash und RAM quetschen? [abgeschlossen]

Ich habe an der Entwicklung einer Funktion für ein bestimmtes Produkt von uns gearbeitet. Es gab eine Anfrage, dieselbe Funktion auf ein anderes Produkt zu portieren. Dieses Produkt basiert auf einem M16C-Mikrocontroller, der traditionell über 64 KB Flash und 2 KB RAM verfügt.

Es ist ein ausgereiftes Produkt und verfügt daher nur noch über 132 Bytes Flash und 2 Bytes RAM.

Um das angeforderte Feature zu portieren (das Feature selbst wurde optimiert), benötige ich 1400 Bytes Flash und ~200 Bytes RAM.

Hat jemand irgendwelche Vorschläge, wie man diese Bytes durch Codeverdichtung abrufen kann? Auf welche spezifischen Dinge achte ich, wenn ich versuche, bereits vorhandenen Arbeitscode zu komprimieren?

Irgendwelche Ideen werden wirklich geschätzt.

Vielen Dank.

Danke an alle für die Vorschläge. Ich werde Sie über meine Fortschritte auf dem Laufenden halten und die Schritte auflisten, die funktioniert haben, und diejenigen, die nicht funktioniert haben.
Ok, hier sind die Dinge, die ich ausprobiert habe und die funktioniert haben: Compiler-Versionen nach oben verschoben. Die Optimierung hatte sich drastisch verbessert, was mir ungefähr 2 KB Flash einbrachte. Durchsuchte die Listendateien, um nach redundanter und ungenutzter Funktionalität (geerbt aufgrund der gemeinsamen Codebasis) für das jeweilige Produkt zu suchen, und gewann etwas mehr Flash.
Für RAM habe ich folgendes gemacht: Ich bin die Map-Datei durchgegangen, um Funktionen/Module zu überprüfen, die den meisten RAM verwenden. Ich fand eine wirklich schwere Funktion (12 Kanäle, jeder mit einer festen Menge an zugewiesenem Speicher) von Legacy-Code, verstand, was er erreichen wollte, und optimierte die RAM-Nutzung, indem ich Informationen zwischen den gemeinsamen Kanälen austauschte. Dies gab mir ~ 200 Bytes, die ich brauchte.
Wenn Sie ASCII-Dateien haben, können Sie eine 8- bis 7-Bit-Komprimierung verwenden. Sparen Sie 12,5 %. Die Verwendung einer ZIP-Datei würde mehr Code erfordern, um sie zu komprimieren und zu entpacken, als sie einfach so zu lassen.

Antworten (17)

Sie haben mehrere Möglichkeiten: Zuerst suchen Sie nach redundantem Code und verschieben ihn in einen einzelnen Aufruf, um die Duplizierung zu beseitigen. Die zweite besteht darin, die Funktionalität zu entfernen.

Sehen Sie sich Ihre .map-Datei genau an und prüfen Sie, ob es Funktionen gibt, die Sie entfernen oder umschreiben können. Stellen Sie außerdem sicher, dass verwendete Bibliotheksaufrufe wirklich benötigt werden.

Bestimmte Dinge wie Division und Multiplikationen können viel Code einbringen, aber die Verwendung von Verschiebungen und eine bessere Verwendung von Konstanten kann den Code kleiner machen. Sehen Sie sich auch Dinge wie String-Konstanten und printfs an. Zum Beispiel printfwird jeder Ihr Rom auffressen, aber Sie können vielleicht ein paar gemeinsame Formatstrings haben, anstatt diese Stringkonstante immer und immer wieder zu wiederholen.

Sehen Sie für den Speicher, ob Sie Globals loswerden und stattdessen Autos in einer Funktion verwenden können. Vermeiden Sie außerdem so viele Variablen wie möglich in der Hauptfunktion, da diese genau wie Globals Speicher verbrauchen.

Danke für die Vorschläge, ich kann die meisten definitiv ausprobieren, außer der für String-Konstanten. Es ist ein reines eingebettetes Gerät ohne Benutzeroberfläche und daher gibt es keine Aufrufe von printf() innerhalb des Codes. In der Hoffnung, dass diese Vorschläge mich dazu bringen sollten, meine 1400 Bytes Flash/200 Bytes RAM zu bekommen, die ich brauche.
@IntelliChick Sie wären erstaunt, wie viele Leute printf() in einem eingebetteten Gerät verwenden, um entweder zum Debuggen oder zum Senden an ein Peripheriegerät zu drucken. Es scheint, als ob Sie es besser wissen, aber wenn jemand vor Ihnen Code für das Projekt geschrieben hat, würde es nicht schaden, danach zu suchen.
Und nur um meinen vorherigen Kommentar zu erweitern, Sie wären auch erstaunt darüber, wie viele Leute Debugging-Anweisungen hinzufügen, aber nie entfernen. Sogar Leute, die #ifdefs machen, werden manchmal immer noch faul.
Super, danke! Ich habe diese Codebasis geerbt, also werde ich auf jeden Fall danach suchen. Ich werde euch über den Fortschritt auf dem Laufenden halten und versuchen zu verfolgen, wie viele Bytes an Speicher oder Flash ich dadurch gewonnen habe, nur als Referenz für alle anderen, die dies in Zukunft tun müssen.
Nur eine Frage dazu - was ist mit verschachtelten Funktionsaufrufen, die von Schicht zu Schicht springen. Wie viel Mehraufwand kommt dadurch hinzu? Ist es besser, die Modularität durch mehrere Funktionsaufrufe beizubehalten oder die Funktionsaufrufe zu reduzieren und einige Bytes zu sparen? Und ist das aussagekräftig?
AFAIR: Der Overhead für eine rekursive Funktion liegt in der Größenordnung von zehn Flops, das hängt davon ab, wie die Kontextänderung von Ihrer speziellen CPU-Architektur gehandhabt wird. Im Allgemeinen bedeutet dies, dass alle lokalen Variablen gespeichert und der Stapelzeiger zurückbewegt werden und weiter.
@intellichick, du musst ein Gleichgewicht finden. Ihr Compiler kann erkennen, wann eine Funktion besser inline ist (je besser der Compiler, desto besser), wodurch der Overhead auf Kosten des ROM entfällt. Mit Compilern wie IAR können Sie die Geschwindigkeit oder Größe optimieren oder ausbalancieren. Es gibt viele erstaunliche Optionen in einem großartigen Compiler, und sie machen großartige Dinge, aber stellen Sie bitte sicher, dass Sie Dinge wie volatile verwenden.

Es lohnt sich immer, sich die Ausgabe von Listendateien (Assembler) anzusehen, um nach Dingen zu suchen, in denen Ihr bestimmter Compiler besonders schlecht ist.

Beispielsweise stellen Sie möglicherweise fest, dass lokale Variablen sehr teuer sind, und wenn die Anwendung einfach genug ist, um das Risiko wert zu sein, kann das Verschieben einiger Schleifenzähler in statische Variablen eine Menge Code einsparen.

Oder die Indizierung von Arrays könnte sehr teuer sein, aber Zeigeroperationen viel billiger. Oder umgekehrt.

Aber ein Blick auf die Assemblersprache ist der erste Schritt.

Es ist sehr wichtig, dass Sie wissen, was Ihr Compiler tut. Sie sollten sehen, was die Division auf meinem Compiler ist. Es bringt Babys zum Weinen (mich eingeschlossen).

Compiler-Optimierungen beispielsweise -Osin GCC bieten die beste Balance zwischen Geschwindigkeit und Codegröße. Vermeiden Sie -O3, da es die Codegröße erhöhen kann.

Wenn Sie dies tun, müssen Sie ALLES erneut testen! Optimierungen können dazu führen, dass funktionierender Code aufgrund neuer Annahmen des Compilers nicht funktioniert.
@Robert, das gilt nur, wenn Sie undefinierte Anweisungen verwenden: zB a = a++ wird in -O0 und -O3 unterschiedlich kompiliert.
@Thomas stimmt nicht. Wenn Sie eine for-Schleife haben, um Taktzyklen zu verzögern, werden viele Optimierer erkennen, dass Sie darin nichts tun, und sie entfernen. Dies ist nur 1 Beispiel.
@thomas O, Sie müssen auch sicherstellen, dass Sie bei flüchtigen Funktionsdefinitionen vorsichtig sind. Optimierer werden diejenigen in die Luft jagen, die glauben, C gut zu kennen, aber die Komplexität atomarer Operationen nicht verstehen.
Alles gute Punkte. Flüchtige Funktionen/Variablen dürfen per Definition NICHT optimiert werden. Jeder Optimierer, der Optimierungen an solchen durchführt (einschließlich Aufrufzeit und Inlining), ist kaputt.
Wir verwenden den IAR C Compiler 3.40 für die M16C-Familie. Ich habe kürzlich von 3.21d auf diese Version aktualisiert, um etwas Speicher für eine Fehlerbehebung zu erhalten, und wir haben durch das Upgrade erheblichen Flash-Speicherplatz (~ 1 KB) erhalten (IAR hatte einige Optimierungsverbesserungen vorgenommen), aber es hat auch einige vorhandene Funktionen beschädigt. Wir haben die Probleme behoben, aber ich bezweifle, dass in diesem Fall viel mehr von der Compiler-Optimierung zu profitieren ist. Danke aber für den Vorschlag!

Überprüfen Sie für RAM den Bereich aller Ihrer Variablen - verwenden Sie Ints, wo Sie ein Zeichen verwenden könnten? Sind die Puffer größer als sie sein müssen?

Code Squeezing ist sehr anwendungs- und codierstilabhängig. Ihre verbleibenden Beträge deuten darauf hin, dass der Code möglicherweise bereits durch einige Quetschungen gegangen ist, was bedeuten kann, dass nur noch wenig zu haben ist.

Schauen Sie sich auch die Gesamtfunktionalität genau an - gibt es etwas, das nicht wirklich verwendet wird und über Bord geworfen werden kann?

Wenn es sich um ein altes Projekt handelt, der Compiler jedoch seitdem entwickelt wurde, kann es sein, dass ein neuerer Compiler möglicherweise kleineren Code erzeugt

Danke Mike! Ich habe das in der Vergangenheit versucht, und der gewonnene Platz wurde bereits genutzt! :) Von IAR C-Compiler 3.21d auf 3.40 hochgestuft.
Ich habe eine weitere Version nach oben verschoben und es geschafft, etwas mehr Flash zu bekommen, um das Feature einzufügen. Ich habe jedoch wirklich Probleme mit dem RAM, der unverändert geblieben ist. :(

Es lohnt sich immer, in Ihrem Compiler-Handbuch nach Optionen zur Platzoptimierung zu suchen.

Für gcc -ffunction-sectionsund -fdata-sectionsmit dem --gc-sectionsLinker-Flag eignen sich gut zum Entfernen von totem Code.

Hier sind einige weitere hervorragende Tipps (ausgerichtet auf AVR)

Funktioniert das tatsächlich? Die Dokumentation sagt: "Wenn Sie diese Optionen angeben, erstellen der Assembler und der Linker größere Objekt- und ausführbare Dateien und sind auch langsamer." Ich verstehe, dass separate Abschnitte für ein Mikro mit Flash- und RAM-Abschnitten sinnvoll sind. Gilt diese Aussage in der Dokumentation nicht für Mikrocontroller?
Meine Erfahrung ist, dass es für AVR gut funktioniert
Dies funktioniert in den meisten Compilern, die ich verwendet habe, nicht gut. Es ist wie die Verwendung des Schlüsselworts register. Sie können dem Compiler sagen, dass eine Variable in ein Register geht, aber ein guter Optimierer wird dies viel besser machen als ein Mensch (manche mögen argumentieren, dass dies in der Praxis als nicht akzeptabel angesehen wird).
Wenn Sie mit der Zuweisung von Speicherorten beginnen, zwingen Sie den Compiler, Dinge an bestimmten Speicherorten zu platzieren, was für fortgeschrittenen Bootloader-Code sehr wichtig ist, aber im Optimierer schrecklich zu handhaben ist. Wenn Sie Entscheidungen dafür treffen, nehmen Sie ihm einen Schritt der Optimierung tun könnte. In einigen Compilern entwerfen sie Abschnitte für den Code, für den er verwendet wird. Dies ist ein Fall, in dem Sie dem Compiler mehr Informationen mitteilen, um Ihre Verwendung zu verstehen. Dies wird helfen. Wenn der Compiler es nicht vorschlägt, tun Sie es nicht.

Sie können den zugewiesenen Stack- und Heap-Speicherplatz untersuchen. Sie können möglicherweise eine beträchtliche Menge an RAM zurückerhalten, wenn einer oder beide überlastet sind.

Meine Vermutung ist, dass es für ein Projekt, das zunächst in 2 KB RAM passt, keine dynamische Speicherzuweisung gibt (Verwendung von malloc, calloc, usw.). Wenn dies der Fall ist, können Sie Ihren Heap ganz loswerden, vorausgesetzt, der ursprüngliche Autor hat etwas RAM für den Heap reserviert.

Sie müssen sehr vorsichtig sein, die Stapelgröße zu reduzieren, da dies zu Fehlern führen kann, die sehr schwer zu finden sind. Es kann hilfreich sein, zunächst den gesamten Stack-Speicherplatz auf einen bekannten Wert zu initialisieren (etwas anderes als 0x00 oder 0xff, da diese Werte häufig bereits vorkommen) und dann das System eine Weile laufen zu lassen, um zu sehen, wie viel Stapelspeicherplatz ungenutzt ist.

Dies sind sehr gute Entscheidungen. Ich werde darauf hinweisen, dass Sie niemals malloc in einem eingebetteten System verwenden sollten.
@Kortuk Das hängt von Ihrer Definition von eingebettet und der auszuführenden Aufgabe ab
@joby, ja, das verstehe ich. In einem System mit 0 Neustarts und dem Fehlen eines Betriebssystems wie Linux kann Malloc sehr, sehr schlecht sein.
Es gibt keine dynamische Speicherzuweisung, keinen Ort, an dem malloc, calloc verwendet wird. Ich habe auch die Heap-Zuweisung überprüft und sie wurde bereits auf 0 gesetzt, sodass keine Heap-Zuweisung vorhanden ist. Die aktuell zugewiesene Stack-Größe beträgt 254 Bytes und die Interrupt-Stack-Größe 128 Bytes.

Verwendet Ihr Code Fließkomma-Mathematik? Möglicherweise können Sie Ihre Algorithmen nur mit ganzzahliger Mathematik neu implementieren und den Overhead der Verwendung der C-Gleitkommabibliothek eliminieren. Beispielsweise können in einigen Anwendungen solche Funktionen wie sin, log, exp durch ganzzahlige polynomische Annäherungen ersetzt werden.

Verwendet Ihr Code große Nachschlagetabellen für Algorithmen wie CRC-Berechnungen? Sie können versuchen, eine andere Version des Algorithmus zu ersetzen, der Werte on-the-fly berechnet, anstatt die Nachschlagetabellen zu verwenden. Die Einschränkung ist, dass der kleinere Algorithmus höchstwahrscheinlich langsamer ist, also stellen Sie sicher, dass Sie genügend CPU-Zyklen haben.

Enthält Ihr Code große Mengen konstanter Daten, z. B. Zeichenfolgentabellen, HTML-Seiten oder Pixelgrafiken (Symbole)? Wenn es groß genug ist (z. B. 10 kB), könnte es sich lohnen, ein sehr einfaches Komprimierungsschema zu implementieren, um die Daten zu verkleinern und bei Bedarf spontan zu dekomprimieren.

Es gibt 2 kleine Nachschlagetabellen, von denen leider keine 10K betragen wird. Und es wird auch keine Fließkomma-Mathematik verwendet. :( Danke für die Vorschläge. Sie sind gut.

Sie können versuchen, den Code in einem kompakteren Stil neu anzuordnen. Es hängt viel davon ab, was der Code tut. Der Schlüssel liegt darin, ähnliche Dinge zu finden und sie in Bezug aufeinander neu zu implementieren. Ein Extrem wäre die Verwendung einer höheren Sprache wie Forth, mit der es einfacher sein kann, eine höhere Codedichte zu erreichen als in C oder Assembler.

Hier ist Forth für M16C .

Legen Sie die Optimierungsstufe des Compilers fest. Viele IDEs haben Einstellungen, die eine Optimierung der Codegröße auf Kosten der Kompilierzeit (oder in einigen Fällen vielleicht sogar der Verarbeitungszeit) ermöglichen. Sie können Code komprimieren, indem sie ihren Optimierer ein paar Mal erneut ausführen, nach weniger häufigen optimierbaren Mustern suchen und eine ganze Reihe anderer Tricks anwenden, die für die gelegentliche/Debug-Kompilierung möglicherweise nicht erforderlich sind. Normalerweise sind Compiler standardmäßig auf eine mittlere Optimierungsstufe eingestellt. Graben Sie in den Einstellungen herum, und Sie sollten in der Lage sein, eine auf Ganzzahlen basierende Optimierungsskala zu finden.

Derzeit auf das Maximum für Größe optimiert. :) Danke für den Vorschlag. :)

Wenn Sie bereits einen professionellen Compiler wie IAR verwenden, werden Sie meiner Meinung nach Schwierigkeiten haben, ernsthafte Einsparungen durch geringfügige Low-Level-Code-Optimierungen zu erzielen - Sie müssen eher darauf achten, Funktionen zu entfernen oder größere Änderungen vorzunehmen Umschreiben von Teilen auf effizientere Weise. Sie müssen ein klügerer Programmierer sein als derjenige, der die Originalversion geschrieben hat ... Was den Arbeitsspeicher betrifft, müssen Sie sich sehr genau ansehen, wie er derzeit verwendet wird, und prüfen, ob es Spielraum für eine überlagerte Verwendung des gleichen Arbeitsspeichers gibt verschiedene Dinge zu verschiedenen Zeiten (Gewerkschaften sind dafür praktisch). Die Standard-Heap- und Stack-Größen von IAR in den ARM / AVR-Größen, die ich tendenziell übertrieben habe, sind also das erste, was Sie sich ansehen sollten.

Danke Mike. Der Code verwendet an den meisten Stellen bereits Unions, aber ich werde mir einige andere Stellen ansehen, wo dies noch helfen könnte. Ich werde mir auch die gewählte Stapelgröße ansehen und sehen, ob sich das überhaupt optimieren lässt.
Woher weiß ich, welche Stapelgröße angemessen ist?

Etwas anderes zu überprüfen - einige Compiler auf einigen Architekturen kopieren Konstanten in den RAM - wird normalerweise verwendet, wenn der Zugriff auf Flash-Konstanten langsam/schwierig ist (z. B. AVR), z. B. der AVR-Compiler von IAR erfordert einen _ _flash-Qualifer, um eine Konstante nicht in den RAM zu kopieren)

Danke Mike. Ja, das hatte ich bereits überprüft - es heißt die Option "Beschreibbare Konstanten" für den M16C IAR C-Compiler. Es kopiert die Konstanten vom ROM ins RAM. Diese Option ist für mein Projekt deaktiviert. Aber ein wirklich gültiger Scheck! Vielen Dank.

Wenn Ihr Prozessor keine Hardwareunterstützung für einen Parameter-/lokalen Stapel hat, der Compiler aber trotzdem versucht, einen Laufzeitparameterstapel zu implementieren, und wenn Ihr Code nicht wiedereintrittsfähig sein muss, können Sie möglicherweise Code speichern Speicherplatz durch statische Zuweisung von Auto-Variablen. In manchen Fällen muss dies manuell erfolgen; in anderen Fällen können Compiler-Direktiven dies tun. Eine effiziente manuelle Zuordnung erfordert die gemeinsame Nutzung von Variablen zwischen Routinen. Eine solche gemeinsame Nutzung muss sorgfältig durchgeführt werden, um sicherzustellen, dass keine Routine eine Variable verwendet, die eine andere Routine als "im Geltungsbereich" betrachtet, aber in einigen Fällen können die Vorteile der Codegröße erheblich sein.

Einige Prozessoren haben Aufrufkonventionen, die einige Parameterübergabestile effizienter machen können als andere. Wenn beispielsweise bei den PIC18-Controllern eine Routine einen einzelnen Ein-Byte-Parameter verwendet, kann dieser in einem Register übergeben werden; braucht es mehr, müssen alle Parameter im RAM übergeben werden. Wenn eine Routine zwei Ein-Byte-Parameter annehmen würde, kann es am effizientesten sein, einen in eine globale Variable zu "übergeben" und dann den anderen als Parameter zu übergeben. Bei weit verbreiteten Routinen können sich die Einsparungen summieren. Sie können besonders wichtig sein, wenn der über global übergebene Parameter ein Ein-Bit-Flag ist oder normalerweise einen Wert von 0 oder 255 hat (da es spezielle Anweisungen gibt, um eine 0 oder 255 im RAM zu speichern).

Auf dem ARM kann das Einfügen globaler Variablen, die häufig zusammen in eine Struktur verwendet werden, die Codegröße erheblich reduzieren und die Leistung verbessern. Wenn A, B, C, D und E separate globale Variablen sind, muss Code, der alle verwendet, die Adresse von jedem in ein Register laden; Wenn nicht genügend Register vorhanden sind, müssen diese Adressen möglicherweise mehrmals neu geladen werden. Wenn sie dagegen Teil derselben globalen Struktur MyStuff sind, kann Code, der MyStuff.A, MyStuff.B usw. verwendet, einfach die Adresse von MyStuff einmal laden. Großer Gewinn.

1.Wenn Ihr Code auf vielen Strukturen beruht, stellen Sie sicher, dass die Strukturmitglieder von denen, die den meisten Speicher belegen, bis zu den wenigsten geordnet sind.

Bsp.: „uint32_t uint16_t uint8_t“ statt „uint16_t uint8_t uint32_t“

Dadurch wird eine minimale Strukturpolsterung gewährleistet.

2.Verwenden Sie gegebenenfalls const für Variablen. Dadurch wird sichergestellt, dass sich diese Variablen im ROM befinden und keinen RAM verbrauchen

Ein paar (vielleicht offensichtliche) Tricks, die ich erfolgreich beim Komprimieren des Codes einiger Kunden angewendet habe:

  1. Merker in Bitfelder oder Bitmasken komprimieren. Dies kann vorteilhaft sein, da boolesche Werte normalerweise als Ganzzahlen gespeichert werden und somit Speicherplatz verschwenden. Dies spart sowohl RAM als auch ROM und wird normalerweise nicht vom Compiler durchgeführt.

  2. Suchen Sie nach Redundanz im Code und verwenden Sie Schleifen oder Funktionen, um wiederholte Anweisungen auszuführen.

  3. Ich habe auch etwas ROM gespart, indem ich viele if(x==enum_entry) <assignment>Anweisungen von Konstanten durch ein indiziertes Array ersetzt habe, indem ich darauf geachtet habe, dass die Enum-Einträge als Array-Index verwendet werden können

Verwenden Sie nach Möglichkeit Inline-Funktionen oder Compiler-Makros anstelle von kleinen Funktionen. Es gibt einen Größen- und Geschwindigkeits-Overhead beim Übergeben von Argumenten und dergleichen, der behoben werden kann, indem die Funktion inline gemacht wird.

Jeder anständige Compiler sollte dies automatisch für Funktionen tun, die nur einmal aufgerufen werden.
Ich habe festgestellt, dass Inlining normalerweise nützlicher für Geschwindigkeitsoptimierungen ist und normalerweise auf Kosten einer erhöhten Größe.
Inlining erhöht normalerweise die Codegröße, außer bei trivialen Funktionen wieint get_a(struct x) {return x.a;}

Ändern Sie die lokalen Variablen so, dass sie dieselbe Größe wie Ihre CPU-Register haben.

Wenn die CPU 32-Bit ist, verwenden Sie 32-Bit-Variablen, auch wenn der Maximalwert nie über 255 steigt. Wenn Sie eine 8-Bit-Variable verwendet haben, fügt der Compiler Code hinzu, um die oberen 24 Bit zu maskieren.

Der erste Ort, an dem ich nachsehen würde, sind die Variablen der for-Schleife.

for( i = 0; i < 100; i++ )

Dies scheint ein guter Ort für eine 8-Bit-Variable zu sein, aber eine 32-Bit-Variable erzeugt möglicherweise weniger Code.

Das kann Code sparen, aber es wird RAM fressen.
Es wird nur dann RAM verbraucht, wenn sich dieser Funktionsaufruf im längsten Zweig der Aufrufverfolgung befindet. Andernfalls wird Stack-Speicherplatz wiederverwendet, den bereits eine andere Funktion benötigt.
Normalerweise wahr, sofern es sich um eine lokale Variable handelt. Wenn der Arbeitsspeicher knapp ist, ist die Größe globaler Variablen, insbesondere von Arrays, ein guter Ausgangspunkt, um nach Einsparungen zu suchen.
Interessanterweise besteht eine andere Möglichkeit darin, vorzeichenlose Variablen durch vorzeichenbehaftete zu ersetzen. Wenn ein Compiler einen vorzeichenlosen Kurzschluss zu einem 32-Bit-Register optimiert, muss er Code hinzufügen, um sicherzustellen, dass sein Wert von 65535 auf Null umbricht. Wenn der Compiler jedoch einen signierten Short zu einem Register optimiert, ist kein solcher Code erforderlich. Da es keine Garantie dafür gibt, was passiert, wenn ein Kurzschluss über 32767 hinaus erhöht wird, müssen Compiler keinen Code ausgeben, um damit umzugehen. Bei mindestens zwei ARM-Compilern, die ich mir angesehen habe, kann signierter Kurzcode aus diesem Grund kleiner sein als unsignierter Kurzcode.