Forum: Compiler & IDEs Optimierung bei if-Abfrage mit kurzem Rumpf


von Heiko (Gast)


Lesenswert?

Moin moin,

Ich möchte mit folgendem C-Code-Schnipsel für ATMega8 ein Signal 
generieren:
1
ISR(TIMER0_OVF_vect)
2
{
3
  uint16_t temp = outputValue; // global deklariert
4
  for(uint8_t i=16; i>0 i--)
5
  {
6
    if(temp & (1<<15))
7
    {
8
      SDA_PORT |= (1<<SDA_PIN);
9
    }
10
    else
11
    {
12
      SDA_PORT ~= (1<<SDA_PIN);
13
    }
14
    // toggle clock
15
    SCL_PORT &= ~(1<<SCL_PIN);
16
    SCL_PORT |= (1<<SCL_PIN);
17
    
18
    // next bit
19
    temp <<= 1;
20
  }
21
}

Wird soweit mit -Os ganz nett übersetzt in sbi, cbi etc. Nur die 
if-else-Abfrage mit den extrem kurzen Schleifenrümpfen (1 
Assembler-Befehl) würde ich in Assembler schneller und vor allem immer 
gleich schnell hinbekommen - das Ergebnis ist nämlich:
1
  sbrs r25,7
2
  rjmp .L6
3
  sbi 53-0x20,4
4
  rjmp .L7
5
.L6:
6
  cbi 53-0x20,4
7
.L7

Das dauert in Zyklen 2(sbrs)+1(sbi)+2(rjmp) für gesetzt, 
1(sbrs)+2(rjmp)+1(cbi) für gelöscht.

Ich würde sowas schreiben wie:
1
  sbrs r25,7
2
  cbi 53-0x20,4
3
  sbrc r25,7
4
  sbi 53-0x20,4

Das dauert in Zyklen 2(sbrs)+1(sbrc)+1(sbi) für gesetzt, 
1(sbrs)+1(cbi)+2(sbrc) für gelöscht - und etwas kleiner ist der Code 
auch noch...

Ist das
a) ein Fall für inline-Assembler?
b) in neueren Versionen von avr-gcc besser geworden (aktuell hier 
4.3.3)?

Oder gibt es eine geschicktere Variante, das in C zu schreiben? Zwei 
getrennte if-Abfragen (einmal auf gesetzt, einmal auf gelöscht) habe ich 
ausprobiert, kompiliert zu dem gleichen Code wie die Variante mit 
if-else - obwohl eine einzelne if-Abfrage auf gelöscht den gleichen Code 
erzeugt wie meine Assembler-Variante.

Oder verrechne ich mich hier gerade und der gcc-Code ist genauso schnell 
wie meiner?

Hat ein Bugreport einen Sinn? Wenn ja, wo?

MfG, Heiko

P.S: Es kommt mir hier nicht nur auf den einen (halben) Zyklus an, 
sondern viel mehr darauf, dass ich gerne high- und low-Bytes gleich lang 
hätte...

von Stefan E. (sternst)


Lesenswert?

Heiko schrieb:
> P.S: Es kommt mir hier nicht nur auf den einen (halben) Zyklus an,
> sondern viel mehr darauf, dass ich gerne high- und low-Bytes gleich lang
> hätte...

Bei deiner handgeschriebenen Variante sind die Bits doch auch nicht 
gleich lang. Die Gesamtdurchlaufzeit ist zwar in beiden Fällen gleich, 
aber cbi und sbi liegen relativ zum Funktionsanfang an unterschiedlichen 
Zeitpunkten. (von der nicht konstanten Interrupt-Latenz mal ganz zu 
schweigen)

von holger (Gast)


Lesenswert?

@Heiko
Bugreport mit deinem fehlerhaften (zwei Fehler) Code?
Ich schmeiß mich weg;)

von Heiko (Gast)


Lesenswert?

Ich merke schon, mal eben schnell etwas abtippen sollte man nicht kurz 
vor Feierabend machen...

Extra für holger der (hoffentlich jetzt vollständig) fehlerkorrigierte 
Code:
1
ISR(TIMER0_OVF_vect)
2
{
3
  uint16_t temp = outputValue; // global deklariert
4
  for(uint8_t i=16; i>0; i--)
5
  {
6
    if(temp & (1<<15))
7
    {
8
      SDA_PORT |= (1<<SDA_PIN);
9
    }
10
    else
11
    {
12
      SDA_PORT &= ~(1<<SDA_PIN);
13
    }
14
    // toggle clock
15
    SCL_PORT &= ~(1<<SCL_PIN);
16
    SCL_PORT |= (1<<SCL_PIN);
17
    
18
    // next bit
19
    temp <<= 1;
20
  }
21
}

Stefan: Du hast Recht, die Dauer des High- bzw. Low-Pegels ist auch bei 
meiner Variante unterschiedlich - da die Kommunikation aber synchron 
abläuft, ist das nicht entscheidend. Wäre eben schön, wenn der Optimizer 
noch dieses bisschen besser optimieren würde...

MfG, Heiko

von Peter D. (peda)


Lesenswert?

Machs doch einfach so:
1
ISR(TIMER0_OVF_vect)
2
{
3
  uint16_t temp = outputValue; // global deklariert
4
  for(uint8_t i=16; i>0; i--)
5
  {
6
    SDA_PORT &= ~(1<<SDA_PIN);
7
    if(temp & (1<<15))
8
    {
9
      SDA_PORT |= (1<<SDA_PIN);
10
    }
11
    // toggle clock
12
    SCL_PORT &= ~(1<<SCL_PIN);
13
    SCL_PORT |= (1<<SCL_PIN);
14
    
15
    // next bit
16
    temp <<= 1;
17
  }
18
}


Peter

von Heiko (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Machs doch einfach so:fasdf

kopfklatsch natürlich :)

MfG, Heiko

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.