2013-11-25 7 views
8

Im msgpcc (GCC für MSP430 Mikrocontroller) manual Autoren schrieben:Warum int preffered anstatt (unsigned) char für kleine Zahlen in MSP430-GCC

Verwendung int statt char oder unsigned char, wenn Sie wollen eine kleine ganze Zahl innerhalb einer Funktion. Der erzeugte Code wird effizienter sein, und in den meisten Fällen wird Speicher nicht wirklich verschwendet.

Warum int ist effizienter?

UPD. Und warum (u)int_fast8_t im MSPGCC zu (unsigned) char definiert, nicht (unsigned) int. Wie ich es verstehe, sollte (u)int_fast*_t auf den effizientesten Typ mit einer ausreichenden Größe definiert werden.

+1

afaik MSP430 keinen Unterschied in Anweisungs Latenz zwischen 8-Bit- und 16-Bit-Operanden hat. Ich bin auch neugierig. –

+1

Ich möchte nur hinzufügen, dass die wunderbare Sache, die ich liebe, über '(u) int_ (schnell | dest) (8 | 16 | 32) _t' ist, dass Sie angeben können, welche Art von Optimierung Sie wollen und in der Regel den Compiler Griff lassen es. Wenn Sie so wenig Platz wie möglich nutzen wollen, und Sie müssen repräsentieren bis zu 20.000, Verwendung '(u) int_least16_t', aber wenn Sie etwas für schwere Berechnung verwenden, die Sie schnell wollen, können Sie' (u) int_fast16_t' weitgehend ohne sich Gedanken darüber machen zu müssen, welchen Typ es unter der Haube gibt. – rjp

Antwort

3

Im Allgemeinen nicht unbedingt spezifisch für diesen Prozessor, hat es mit Vorzeichenerweiterung und Maskierung zu tun, erfordern zusätzliche Anweisungen getreulich den C-Quellcode zu implementieren. Ein vorzeichenbehafteter 8-Bit-Wert in einem 16- oder 32- oder 64-Bit-Prozessor kann zusätzliche Anweisungen zum Signieren von Extend beinhalten. Ein 8-Bit auf 32-Bit-Prozessor hinzufügen könnte beinhalten zusätzliche Anweisungen und mit 0xFF usw.

Sie sollten ein paar einfache Experimente durchführen, es dauerte ein paar Wiederholungen, aber ich traf schnell etwas, das einen Unterschied zeigte.

unsigned int fun (unsigned int a, unsigned int b) 
{ 
    return(a+b)<<3; 
} 

unsigned char bfun (unsigned char a, unsigned char b) 
{ 
    return(a+b)<<3; 
} 


int sfun ( int a, int b) 
{ 
    return(a+b)<<3; 
} 

char sbfun ( char a, char b) 
{ 
    return(a+b)<<3; 
} 

produziert

00000000 <fun>: 
    0: 0f 5e   add r14, r15 
    2: 0f 5f   rla r15  
    4: 0f 5f   rla r15  
    6: 0f 5f   rla r15  
    8: 30 41   ret   

0000000a <bfun>: 
    a: 4f 5e   add.b r14, r15 
    c: 4f 5f   rla.b r15  
    e: 4f 5f   rla.b r15  
    10: 4f 5f   rla.b r15  
    12: 30 41   ret   

00000014 <sfun>: 
    14: 0f 5e   add r14, r15 
    16: 0f 5f   rla r15  
    18: 0f 5f   rla r15  
    1a: 0f 5f   rla r15  
    1c: 30 41   ret   

0000001e <sbfun>: 
    1e: 8f 11   sxt r15  
    20: 8e 11   sxt r14  
    22: 0f 5e   add r14, r15 
    24: 0f 5f   rla r15  
    26: 0f 5f   rla r15  
    28: 0f 5f   rla r15  
    2a: 4f 4f   mov.b r15, r15 
    2c: 30 41   ret   

Die msp430 Wort und Byte-Versionen der Befehle hat so eine einfache Addition oder Subtraktion muß nicht den Ausschnitt zu tun oder Verlängerung zu unterzeichnen, die man erwarten würde bei der Verwendung kleiner als registrieren Größe Variablen. Als Programmierer wissen wir vielleicht, dass wir sbfun nur mit sehr kleinen Zahlen füttern würden, aber der Compiler tut das nicht und muss unseren Code wie geschrieben treu umsetzen und mehr Code zwischen sfun und sbfun generieren. Es ist nicht schwer, diese Experimente mit verschiedenen Compilern und Prozessoren zu machen, um dies in Aktion zu sehen. Der einzige Trick besteht darin, Code zu erstellen, den der Prozessor nicht einfach zu lösen braucht.

weiteres Beispiel

unsigned int fun (unsigned int a, unsigned int b) 
{ 
    return(a+b)>>1; 
} 

unsigned char bfun (unsigned char a, unsigned char b) 
{ 
    return(a+b)>>1; 
} 

produziert

00000000 <fun>: 
    0: 0f 5e   add r14, r15 
    2: 12 c3   clrc    
    4: 0f 10   rrc r15  
    6: 30 41   ret   

00000008 <bfun>: 
    8: 4f 4f   mov.b r15, r15 
    a: 4e 4e   mov.b r14, r14 
    c: 0f 5e   add r14, r15 
    e: 0f 11   rra r15  
    10: 4f 4f   mov.b r15, r15 
    12: 30 41   ret   
+0

+1 ausgegeben werden, obwohl es ist erwähnenswert, dass größere Typen für Parameter angeben können zu einer erhöhten Stackverbrauch führen kann, die Ich vermute, möglicherweise ein Problem, wenn Ressourcen eingeschränkt sind. – Groo

+0

Ja, es ist alles ein Leistungs- und Optimierungsspiel ... Wollte einige winzige Samen pflanzen, warum kleiner nicht unbedingt besser ist (relativer Begriff). –

5

Eine allgemeine Faustregel ist, dass CPUs am schnellsten bei Ganzzahlen ihrer ursprünglichen Wortgröße arbeiten.

Das ist natürlich völlig Architektur abhängig, siehe die Antworten für mehr Klarheit in diesem Punkt zu this similar question.

5

TI hat eine Application Note zum Thema für ihre Tiva-C (formal Stellaris) MCUs veröffentlicht.

Im Abschnitt „Einleitung“ stellt eine Tabelle, die eine Liste der Faktoren Leistung und Größe zu beeinflussen. Eine Faktor Label Variable Größe besagt, dass Verwendung von Variablen kleiner als optimal zusätzliche Anweisungen oder unsign zu verlängern zu unterzeichnen bedeuten kann ....

Auch unter dem Abschnitt „Größe der Variablen“, heißt es:.

„Wenn die lokalen Variablen kleiner als die Registergröße sind, dann wird zusätzlicher Code in der Regel erforderlich Auf einem Stellaris Teil, das bedeutet, dass Lokale Variablen der Größe Byte und Halbwort (char bzw. short int) benötigen zusätzlichen Code, da Code, der von einem 8-Bit- oder 16-Bit-Mikrocontroller portiert wurde, möglicherweise kleinere Größen verwendet hat (um das zu große Problem zu vermeiden) dass ein solcher Code langsamer läuft und mehr Code benötigt als benötigt wird. "

Bitte sehen: http://www.ti.com/lit/an/spma014/spma014.pdf

Die folgende wird vom Compiler behandelt, ist aber immer noch relevant für die Frage auf der Hand:

Der MSP430 ist ein 16-Bit-Mikroprozessor. Ein Zeichen besteht nur aus 8 Bits und würde ein Packen erfordern, um sicherzustellen, dass alle Wörter ausgerichtet sind. Zum Beispiel würden 3 Zeichen nicht korrekt im Speicher ausgerichtet. Verwenden Sie stattdessen eine Ganzzahl mit 16 Bits, die immer ausgerichtet wird.

Wenn Sie variable Größen verwenden, die ein Vielfaches von 16 sind (z. B. 16 und 32), können Sie den Speicher auch effizienter nutzen. Sie werden nicht mit Auffüllen enden, um den Speicher auszurichten.

+1

Die Aufgabe des Compilers ist keine Ausrichtungsprobleme zu gewährleisten, und es wird nicht mehr Platz verschwenden, als würden Sie durch einen 16-Bit-Variable mit dem einen 8-Bit eines gearbeitet haben würde. Sie haben das Padding im Wesentlichen von einem impliziten Prozess in einen expliziten Prozess verschoben. –

+0

@CoryNelson Ich stimme zu - es ist compiler-bezogen und nicht Laufzeit. Sie stimmen jedoch nicht mit Ihrer Aussage überein, dass nicht mehr Speicher benötigt wird. Haben Sie versucht, mit GCC und IAR zu kompilieren? Sie paddern beide unterschiedlich und Sie haben eine radikal andere Code-Größe, wenn Sie nicht vorsichtig sind (ohne Optimierung). Dies scheint auf das Auffüllen zurückzuführen zu sein. – bblincoe

1

int entspricht die nativen Größe des Prozessors in Frage (16 Bit), so dass, wenn Sie für ein Geschäft bitten, zu einer unsigned char Variable kann der Compiler zusätzlichen Code emittieren, um sicherzustellen, dass der Wert zwischen 0 und 255 ist.

+1

Ich denke du liegst falsch. Der Compiler erzeugt keinen Code zum Umbrechen von 255 auf 0. MSP430 hat byte-orientierte Anweisungen und der Compiler wird nur die 'add.b'-Anweisung anstelle von' add' verwenden. –

+0

@Corvus Das ist richtig. Der MSP430 verfügt über 27 Befehle, wobei die meisten Befehle in .B (8-Bit) und .W (16-Bit) Suffixversionen verfügbar sind. – bblincoe

+0

OK, guter Punkt in diesem Fall, nicht so auf anderen Prozessoren. Es ist zu lange her, seit ich MSP430ing war! Ich bin sicher, es gibt auch andere Fälle (als nur eine einfache Erhöhung), wo ein zusätzliches UND müsste aber ... –