AVR Inline-Optimierung

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

AVR Inline-Optimierung

Beitrag von Mathias »

Wen ich folgenden Test mache, verbraucht ein Aufruf einer Inline-Procedure mehr Speicher als wen man dies selbst direkt reinschreibt.

Code: Alles auswählen

  procedure sei; assembler; inline;
  asm
           Sei
  end;


Code: Alles auswählen

begin
  Sei;  // Braucht 8 Byte
  asm cli end; // Braucht 2 Byte;
end.

Ich dachte immer, wen man eine Procedure, vor allem eine mit assembler, werde direkt in den Haupt-Code kompiliert.

Somit empfiehlt es sich, es selbst direkt in den Code zu schreiben, vor allem bei so was einfachen wie cli und sei.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: AVR Inline-Optimierung

Beitrag von Timm Thaler »

Auch hier wieder der Verweis auf die *.s-Dateien.

Code: Alles auswählen

.section .text.n_gh_init_ss_cli
.globl   GH_INIT_ss_CLI
GH_INIT_ss_CLI:
   cli
   ret
.Le0:
   .size   GH_INIT_ss_CLI, .Le0 - GH_INIT_ss_CLI
 
.section .text.n_gh_init_ss_sei
.globl   GH_INIT_ss_SEI
GH_INIT_ss_SEI:
   sei
   ret
.Le1:
   .size   GH_INIT_ss_SEI, .Le1 - GH_INIT_ss_SEI


Ja, die Prozeduren werden als solche abgelegt und mit call und return abgerufen. Was auch noch den Nachteil hat, dass sie 7 zusätzliche Takte für Einsprung und Rückkehr benötigen.

Direktes Schreiben des Inline-Assemblers in andere Prozeduren kann aber auch den Nachteil haben, dass der Compiler einen Haufen Register- und Stackpointersicherung extra betreibt und die Prozedur aufbläht. Trifft bei Dir nicht zu, weil da nichts drumrum ist, was er sichern müsste.

Ich bin da noch nicht ganz dahintergestiegen, wann was günstiger ist.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: AVR Inline-Optimierung

Beitrag von Mathias »

Auch hier wieder der Verweis auf die *.s-Dateien.
Woher nehmen und nicht stehlen.
Hier ./lib/avr-embedded kann ich keine finden, dort hat es nur 2 Dateien.

./lib/avr-embedded/Project1.compiled
./lib/avr-embedded/Project1.o

Ich bin da noch nicht ganz dahintergestiegen, wann was günstiger ist.
Könnte sein, das der AVR-Compiler noch stark in der Beta ist ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: AVR Inline-Optimierung

Beitrag von Timm Thaler »

Bei den benutzerdefinierten Compilereinstellungen
-Cpavr5
-Wpatmega328p
-al
stehen?

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: AVR Inline-Optimierung

Beitrag von kupferstecher »

Nach meiner Beobachtung funktioniert "inline" nur bei normalen Routinen und nicht bei Assembler-Routinen. Ist vermutlich einfach nicht implementiert.

Cli und Sei sind zwar wichtig, aber die paar Takte extra dürften kaum kritisch sein.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: AVR Inline-Optimierung

Beitrag von Mathias »

Timm Thaler hat geschrieben:Bei den benutzerdefinierten Compilereinstellungen
-Cpavr5
-Wpatmega328p
-al
stehen?

Dank dem -al gibt es eine *.s .

Für was hast du noch den Parameter -Cpavr5 eingetragen ?
Ich habe/hatte bei mir nur -Wpatmega328p.

Das mit dem -al werde ich noch im Tutorial erste Schritte ergänzen, ich denke dies ist recht nützlich.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: AVR Inline-Optimierung

Beitrag von Timm Thaler »

Wenn Du rechts daneben auf "Alle Einstellungen" klickst, bekommst Du ein Fenster, was da alles einstellbar ist.

a heisst, dass Assembler-File (die *.s) wird nicht gelöscht. al heisst, die Zeilenzahlen aus dem Sourcecode stehen im Assemblerfile, das hilf die Stelle schneller zu finden.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: AVR Inline-Optimierung

Beitrag von Mathias »

Ich habe mal versucht, für ATtiny dem Parameter -Cpavr25 und -WpATTINY2313A einzutragen.
Dies bringt nicht, wen der Cross-Compiler mit 5 kompiliert wurde.
Ich dachte schon, man könnte sich die Umkomplierung mit fpcupdelux sparen.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: AVR Inline-Optimierung

Beitrag von Timm Thaler »

Mathias hat geschrieben:Somit empfiehlt es sich, es selbst direkt in den Code zu schreiben, vor allem bei so was einfachen wie cli und sei.


Lösung ohne Prozeduraufruf oder Inline-Assembler:

Code: Alles auswählen

avr_cli;
avr_sei;


Die Unit intrinsics muss eingebunden werden. cli und sei werden dann direkt eingefügt.

FPK
Beiträge: 65
Registriert: Mi 21. Mai 2008, 19:38
Wohnort: Erlangen

Re: AVR Inline-Optimierung

Beitrag von FPK »

kupferstecher hat geschrieben:Nach meiner Beobachtung funktioniert "inline" nur bei normalen Routinen und nicht bei Assembler-Routinen. Ist vermutlich einfach nicht implementiert.


Ist auch extrem schwierig bis gar nicht zu implementieren, denn das würde heissen, dass der Compiler "verstehen" muss, was in der Assembler-Routine passiert.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: AVR Inline-Optimierung

Beitrag von Mathias »

Die Unit intrinsics muss eingebunden werden. cli und sei werden dann direkt eingefügt.

Da hast du etwas sehr interessantes entdeckt, das macht die AVR-Programmierung wider etwas einfacher. :wink:

Bitte teile solche Sachen im Forum mit, dann hat man die Möglichkeit das Wiki auch zu ergänzen. :wink:
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: AVR Inline-Optimierung

Beitrag von kupferstecher »

FPK hat geschrieben:Ist auch extrem schwierig bis gar nicht zu implementieren, denn das würde heissen, dass der Compiler "verstehen" muss, was in der Assembler-Routine passiert.

Kannst du genauer was dazu schreiben, warum muss der Compiler die Assembler-Routine verstehen? Ich würde ja keine Optimierungen vom Compiler auf Assemblerebene erwarten, er soll nur den gesamten Assemblerblock in den aufrufenden Code packen, einfach, um sich den Call zu sparen. Dem Compiler sind sogar die verwendeten Register bekannt, es könnten je nach Anwendung auch noch Stackoperationen eingespart werden. Selbst Variablenaufrufe sollten dem Compiler bekannt sein, damit könnten Werte in Registern gehalten werden (vermutlich aufwendiger).

An anderer Stelle habe ich gelesen, dass man Inlineassembler in Asm-Funktionen packen sollte, da Funktionen mit versprengten Assemblerblöcken nicht oder schlecht optimiert werden würden. Vermutlich hängen beide Themen zusammen?

FPK
Beiträge: 65
Registriert: Mi 21. Mai 2008, 19:38
Wohnort: Erlangen

Re: AVR Inline-Optimierung

Beitrag von FPK »

kupferstecher hat geschrieben:
FPK hat geschrieben:Ist auch extrem schwierig bis gar nicht zu implementieren, denn das würde heissen, dass der Compiler "verstehen" muss, was in der Assembler-Routine passiert.

Kannst du genauer was dazu schreiben, warum muss der Compiler die Assembler-Routine verstehen? Ich würde ja keine Optimierungen vom Compiler auf Assemblerebene erwarten, er soll nur den gesamten Assemblerblock in den aufrufenden Code packen, einfach, um sich den Call zu sparen. Dem Compiler sind sogar die verwendeten Register bekannt, es könnten je nach Anwendung auch noch Stackoperationen eingespart werden. Selbst Variablenaufrufe sollten dem Compiler bekannt sein, damit könnten Werte in Registern gehalten werden (vermutlich aufwendiger).


Registernutzung ist eine Sache, die man vielleicht in den Griff kriegt. Aber man kann eben in Assembler unendlich Gemeinheiten schreiben, die den Compiler bzw. den Optimierer aus dem Tritt bringen, angefangen bei Aliasing oder unerwarteten Stackmanipulationen. Klar kann man zig Regeln aufstellen, wie eine inlinebare Assemblerfunktion auszusehen hat, aber da öffnet man eine Büchse der Pandora. Ich bin deswegen auch der Meinung: der Nutzer einer Entwicklungssystem darf kein Assembler benötigen, falls doch, ist was am Compiler/der Sprache falsch (natürlich sollte auch die Laufzeitbibliothek so wenig wie möglich Assembler enthalten, leider kommt man da manchmal nicht drumherum).

An anderer Stelle habe ich gelesen, dass man Inlineassembler in Asm-Funktionen packen sollte, da Funktionen mit versprengten Assemblerblöcken nicht oder schlecht optimiert werden würden. Vermutlich hängen beide Themen zusammen?


Ja.

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: AVR Inline-Optimierung

Beitrag von Timm Thaler »

FPK hat geschrieben:Ich bin deswegen auch der Meinung: der Nutzer einer Entwicklungssystem darf kein Assembler benötigen, falls doch, ist was am Compiler/der Sprache falsch.


Am PC möchte ich auch nicht (mehr) mit Assembler rumfummeln. Allerdings konnte man nur so in Turbo-Pascal einen Grafikbildschirm mehr als 16 Farben bekommen. ;-)

Allerdings geht es hier um Mikrocontroller, und da musst Du mitunter selbst am gut abgehangenen GCC mit Asm arbeiten.

Und der Pascal-Compiler macht mitunter absolut komische Sachen. Einmal den Datentyp von Unsigned auf Signed gewechselt, und Du bekommst statt einer wenige µsec dauernden Hardware-Multiplikation (in 16bit) eine Software-Multiplikation mit 100-facher Dauer hingeklatscht.

Code: Alles auswählen

# [39] c_u32 := a_u16 * b_u16;
   lds   r18,(U_sMT_MATH_ss_A_U16)
   lds   r19,(U_sMT_MATH_ss_A_U16+1)
   lds   r22,(U_sMT_MATH_ss_B_U16)
   lds   r23,(U_sMT_MATH_ss_B_U16+1)
   mul   r18,r22
   movw   r20,r0
   mul   r19,r22
   add   r21,r0
   mul   r18,r23
   add   r21,r0
   clr   r1
   mov   r18,r1
   mov   r19,r1
   sts   (U_sMT_MATH_ss_C_U32),r20
   sts   (U_sMT_MATH_ss_C_U32+1),r21
   sts   (U_sMT_MATH_ss_C_U32+2),r18
   sts   (U_sMT_MATH_ss_C_U32+3),r19
# [40] c_i32 := a_u16 * b_i16;
   lds   r18,(U_sMT_MATH_ss_B_I16)
   lds   r19,(U_sMT_MATH_ss_B_I16+1)
   mov   r20,r1
   sbrc   r19,7
   com   r20
   mov   r21,r20
   lds   r22,(U_sMT_MATH_ss_A_U16)
   lds   r23,(U_sMT_MATH_ss_A_U16+1)
   mov   r24,r1
   mov   r25,r1
   call   fpc_mul_longint
   sts   (U_sMT_MATH_ss_C_I32),r22
   sts   (U_sMT_MATH_ss_C_I32+1),r23
   sts   (U_sMT_MATH_ss_C_I32+2),r24
   sts   (U_sMT_MATH_ss_C_I32+3),r25


Es ist ja richtig, dass die obere Mul nur 16 Bit liefert, Überlauf wird einfach abgeschnitten, während die untere echte 32 Bit hat. Aber es ist für den Programmierer nicht ersichtlich, warum der Compiler mal so und mal so entscheidet.

Oder Divison / 256. Immerhin erkennt der Compiler, dass er keine Software-Division aufrufen muss. Allerdings macht er dann ein Shift über 8 Bits draus. Dabei muss er nur ein paar Bytes umsortieren.

Auch sehr beliebt, Werte erstmal in ein Register laden, um sie dann in ein anderes Register umzuladen, mit dem gerechnet wird.

Wenn es dann schnell gehen soll, muss man einfach von Hand ran.

FPK
Beiträge: 65
Registriert: Mi 21. Mai 2008, 19:38
Wohnort: Erlangen

Re: AVR Inline-Optimierung

Beitrag von FPK »

Timm Thaler hat geschrieben:
FPK hat geschrieben:Ich bin deswegen auch der Meinung: der Nutzer einer Entwicklungssystem darf kein Assembler benötigen, falls doch, ist was am Compiler/der Sprache falsch.


Und der Pascal-Compiler macht mitunter absolut komische Sachen. Einmal den Datentyp von Unsigned auf Signed gewechselt, und Du bekommst statt einer wenige µsec dauernden Hardware-Multiplikation (in 16bit) eine Software-Multiplikation mit 100-facher Dauer hingeklatscht.

Es ist ja richtig, dass die obere Mul nur 16 Bit liefert, Überlauf wird einfach abgeschnitten, während die untere echte 32 Bit hat. Aber es ist für den Programmierer nicht ersichtlich, warum der Compiler mal so und mal so entscheidet.


Dann solltest Du Dich mal mit den Typkonvertierungsregeln in (Free) Pascal beschäftigen, das ist relativ fundamental und sollte jedem Programmierer bewusst sein. Kurzform: Der Typ des Ergebnisses einer Operation hängt immer von den Typen der Operanden ab, bei arithmetischen Operationen gilt eigentlich immer, dass der Typ gewählt wird, in den beiden Operanden passen, bei word*word ist das eben word, bei word*integer ist das longint. Damit ist klar, dass word*integer eine 32 Bit Operation verursacht. An was Du das Ergebnis zuweisst, spielt semantisch keine Rolle, der Compiler kann natürlich versuchen, bei entsprechenden Randbedingungen eine aufwendige Operation zu vermeiden und integer*word doch nur mit 16 Bit rechnen lassen.

Oder Divison / 256. Immerhin erkennt der Compiler, dass er keine Software-Division aufrufen muss. Allerdings macht er dann ein Shift über 8 Bits draus. Dabei muss er nur ein paar Bytes umsortieren.


Stimmt für trunk jedenfalls nicht mehr:

Code: Alles auswählen

 
PsPROGRAM_ss_DIVIDE256sWORDssWORD:
# Var $result located in register r18
# [tdiv.pp]
# [6] begin
   mov   r24,r25
# Var w located in register r24
# [7] result:=w div 256;
   mov   r25,r1
# Var $result located in register r24
# [8] end;
   ret
 


Auch sehr beliebt, Werte erstmal in ein Register laden, um sie dann in ein anderes Register umzuladen, mit dem gerechnet wird.


Sowas lässt sich oft im Compiler beheben, wenn konkreter Code vorliegt.

Wenn es dann schnell gehen soll, muss man einfach von Hand ran.


Gibt wenige Beispiele wo das gilt, die oben jedenfalls nicht :) Wenn irgendwo komischer Code erzeugt wird, Bugreport erstellen, lässt sich dann meist lösen, da profitieren dann alle Programme davon.

Antworten