Forum: Mikrocontroller und Digitale Elektronik Multiply-Add Instruktion ist langsam


von Martin O. (ossi-2)


Lesenswert?

Ich programmiere derzeit auf dem teensy 36 Board, das hat ne ARM Cortex 
M4 CPU und bei den Fliesspunktbefehlen habe ich folgendes festgestellt:
Addition (Befehl VADD) und Multiplikation(Befehl VMUL) brauchen jweils 1 
Zyklus. Die Multiply-Add Instruction (VMLA) braucht 3 Zyklen. Damit ist 
der VMLA Befehl langsamer als wenn ich getrennt MUL und ADD mache. 
Welchen Sinn ergibt das? Welchen Vorteil hat die VMLA Instruktion?

Im folgenden Beispiel
1
 for(int k=0 ; k<nn ; k++){
2
    fx += fy * fz ;
3
    fy += 1.2 ;
4
    fz += 1.3 ;
5
    }
benutzt der GCC compiler die VMLA Instruktion für fx += fy * fz ;

von (prx) A. K. (prx)


Lesenswert?

Das Ergebnis ist genauer, wenn erst abschliessend gerundet wird:
https://de.wikipedia.org/wiki/Fused_multiply-add

Es wird ein Zwischenregister eingespart.

PS: Lies auch die Fussnoten: "Floating-point arithmetic data processing 
instructions, such as add, subtract, multiply, divide, square-root, all 
forms of multiply with accumulate, as well as conversions of all types 
take one cycle longer if their result is consumed by the following 
instruction."
  FMUL
  FADD
  ..use result..
hat 2 Straftakte drin und kommt damit auf 4 Takte, während
  VMLA
  ..use result..
nur einen Straftakt hat und damit gleichauf liegt.

von (prx) A. K. (prx)


Lesenswert?

FPUs mit Pipelining könnten mit FMA effizienter umgehen als mit 
getrennten Operationen, und damit schneller sein. Das betrifft den M4F 
nicht, aber der Befehlssatz ist möglicherweise darauf ausgerichtet.

von Martin O. (ossi-2)


Lesenswert?

Das klingt schon mal logisch. Besten Dank.

Ich habe die Reihenfolge der Statements in der Schleife jetzt mal 
geändert (siehe unten). Ich dachte ich könnte so einen Strafzyklus 
notwendig machen, weil der vfma Befehl jetzt das Resultat des vadd 
Befehls vor ihm braucht. Aber der Compiler hat geschickterweise die subs 
Instruktion vorgezogen.
1
  int32_t start=micros() ;
2
  for(int k=0 ; k<nn ; k++){ 
3
    fy += 1.2 ;
4
    fz += 1.3 ;
5
    fx += fy * fz ;
6
    }
7
  int32_t stop=micros() ;
8
/*
9
   50e: ee37 7a06   vadd.f32  s14, s14, s12    1 Zyklus
10
   512: ee77 7aa5   vadd.f32  s15, s15, s11    1 Zyklus
11
   516: 3b01        subs  r3, #1               1 Zyklus
12
   518: eee7 6a27   vfma.f32  s13, s14, s15    3 Zyklen
13
   51c: d1f7        bne.n 50e <setup+0x9e>     2 Zyklen
14
*/

Jetzt verstehe ich folgendes nicht: Meine Laufzeitmessung sagt, dass ein 
Schleifendurchlauf 7 Zyklen braucht. Ich komme aber auf 8 Zyklen, wo 
mache ich einen Fehler ?

von void (Gast)


Lesenswert?

Martin O. schrieb:
> Ich komme aber auf 8 Zyklen, wo mache ich einen Fehler ?

Wilde Vermutung: bne.n hat unterschiedliche Ausführungszeit für branch 
taken und branch not taken

von (prx) A. K. (prx)


Lesenswert?

void schrieb:
> Wilde Vermutung: bne.n hat unterschiedliche Ausführungszeit für branch
> taken und branch not taken

Die hat er mit 2 Takten schon eingerechnet. Aber ohne nun beim M4 
konkret nachgesehen zu haben könnte es sein, dass das Gesamtsystem ihm 
noch einen Takt für nichtsequentiellen Zugriff oder schlecht aligntes 
Sprungziel zusätzlich drauf haut.

von void (Gast)


Lesenswert?

A. K. schrieb:
> Die hat er mit 2 Takten schon eingerechnet. Aber ohne nun beim M4
> konkret nachgesehen zu haben

Ja, und wenn er für branch taken nur einen Takt braucht. So herum war es 
gemeint. War halt eine Idee zum nachschauen. Das nehme ich ihm aber 
nicht ab.

von (prx) A. K. (prx)


Lesenswert?

Und ich hatte das verkehrt herum gelesen...

von Karl (Gast)


Lesenswert?

Sorry für OT, aber warum schaust du dir das so genau an? Ich meine, es 
ist interessant aber irgendwie auch brotlos auf den größeren 
Controllern. Mich würde es einfach interessieren.

von Jim M. (turboj)


Lesenswert?

Martin O. schrieb:
> Ich komme aber auf 8 Zyklen, wo
> mache ich einen Fehler ?

Bei der Adresse. 0x50E ist nicht auf 4 Bytes ausgerichtet - das gibt 
hier den Staftakt, denn dort steht ein 4 Bytes langer Befehl.

Mach mal ein __NOP(); vor die Schleife..

von (prx) A. K. (prx)


Lesenswert?

So hatte ich das auch erst gelesen. Er beschwert sich aber über einen 
Takt zu wenig.

von Gerd E. (robberknight)


Lesenswert?

A. K. schrieb:
> So hatte ich das auch erst gelesen. Er beschwert sich aber über einen
> Takt zu wenig.

vielleicht braucht das VMLA doch weniger als 3 Takte und die genannten 
Effekte (alignment,...) traten bei der allerersten Messung auf?

von (prx) A. K. (prx)


Lesenswert?

Die Pipeline widmet sich bereits dem Sprungbefehl und dessen Ziel, 
während der VMLA Befehl noch läuft. Das ist zwar spekulativ, aber 
dadurch reduziert sich die Laufzeit der Execute-Stage des Sprungbefehls 
auf einen Takt zur Bestätigung.

https://community.arm.com/developer/ip-products/processors/f/cortex-m-forum/8969/question-about-the-pipeline-clock-cycle-and-machine-cycle-in-cortex-m-series

von W.S. (Gast)


Lesenswert?

Martin O. schrieb:
> Welchen Sinn ergibt das? Welchen Vorteil hat die VMLA Instruktion?

Normalerweise macht man damit Filter und optimiert selbige.

Das heißt - da man ohnehin beim Filtern 2 Datenströme bearbeiten muß (I 
und Q) - kann man das Ganze auch verschachteln und die Schleife zum Teil 
aufrollen, womit man den Taktverbrauch für das Schleifenzählen und 
Springen reduziert.

Also mal ganz grobschlächtig skizziert so:
 for ..
{ sumI = sumI + pkoeffI++ * psamplI++;
  sumQ = sumQ + pkoeffQ++ * psamplQ++;
  sumI = sumI + pkoeffI++ * psamplI++;
  sumQ = sumQ + pkoeffQ++ * psamplQ++;
  ...usw. immer I und Q abwechselnd
}
Wenn man z.B. in der Schleife 8 solche IQ Paare drin hat, dann ist der 
Aufwand in Takten für das Schleifenzählen nur noch 1/8 dessen, was man 
mit nur einem Paar braucht.

W.S.

von Martin O. (ossi-2)


Lesenswert?

In anderen Beispielen benötigte die CPU immer 2 Takte für den Sprung. 
Daher zähle ich den Sprung hier auch mit 2 Zyklen.

Das ganze mache ich aus purer Neugirde und um herauszufinden welche 
Konstrukte welchen Aufwand haben.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.