www.mikrocontroller.net

Forum: Compiler & IDEs Frage bezüglich Optimierung


Autor: Ulrich (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Weshalb erzeugt diese Zeile:
PORTB |= _BV(1)|_BV(2);
2Bytes mehr Code als diese 2 Zeilen:
sbi(PORTB, 1);
sbi(PORTB, 2);

Für sbi verwende ich das define von  <compat/deprected.h>

Autor: Hagen Re (hagen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
im ersten Code muß der Compiler den PORTB in ein Register laden, danach 
mit einem Wert odern und zurück in PORTB schreiben

in  reg, PORTB
ldi temp, 3
or  reg temp
out PORTB, reg

du schreibst ja im C soure auch

PORTB = PORTB | (x);

im zweiten Falle kann der Compiler einen speziellen ASM Opcode nutzen 
der immr nur 1 Bit eines Ports gleichzeitig löschen/setzen kann.

Da dur nur 2 Bits auf diese weise änderst braucht man du 2 Befehle.

Angenommen do wolltest

PORTB |= _BV(1) | _BV(2) | _BV(3) | _BV(4) | _BV(5);

setzen, dann wäre deine 2'te Methode um ein Takt langsammer.

Gruß hagen

Autor: Ulrich (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bei mir benötigt aber z.B.  auch:
PORTB |= _BV(PB1)|_BV(PB2)|_BV(PB3)|_BV(PB4)|_BV(PB5);
im Gegensatz zu:
PORTB |= _BV(PB1)|_BV(PB2)|_BV(PB3)|_BV(PB4);
2 Bytes mehr...

Autor: Μαtthias W. (matthias) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

dein erster Fall dürfte eine fehlende Optimierung des GCC sein die eben 
nur zuschlägt wenn du zwei Bits setzt. Du kannst natürlich auch

PORTB |= _BV(1);
PORTB |= _BV(2);

schreiben. Das sollte zum gleichen Code wie die sbi Makros führen. Den 
zweiten Fall das

PORTB |= _BV(PB1)|_BV(PB2)|_BV(PB3)|_BV(PB4)|_BV(PB5);

mehr Code erzeugt als

PORTB |= _BV(PB1)|_BV(PB2)|_BV(PB3)|_BV(PB4);

kann ich hier (WINAVR 20060125) nicht nachvollziehen.

Matthias

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> dein erster Fall dürfte eine fehlende Optimierung des GCC sein die eben
> nur zuschlägt wenn du zwei Bits setzt.

Die Optimierung kann er möglicherweise schon, aber PORTB ist volatile. 
Das heißt, das bei

PORTB |= _BV(1) | BV(2);

das Register nicht mehr als einmal gelesen/geschrieben werden darf.

Autor: Μαtthias W. (matthias) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus wrote:
>> dein erster Fall dürfte eine fehlende Optimierung des GCC sein die eben
>> nur zuschlägt wenn du zwei Bits setzt.
>
> Die Optimierung kann er möglicherweise schon, aber PORTB ist volatile.
> Das heißt, das bei
>
> PORTB |= _BV(1) | BV(2);
>
> das Register nicht mehr als einmal gelesen/geschrieben werden darf.

Echt? Ist volatile derart definiert? Wo steht denn das? (ernstgemeinte 
Frage!) Mir war volatile nur als modifier bekannt das sich der Wert der 
Variable auch außerhalb des aktuellen Programmfluß ändern kann und der 
Compiler deshalb einige Optimierung nicht vornehmen darf. Von einem 
einzigen Zugriff ist mir nichts bekannt. PORTB |= _BV(1) | BV(2); ist eh 
nich atomar.

Matthias

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich sach ma, der Ausdruck rechts vom = muß immer zuerst berechnet 
werden, unabhängig vom volatile.

Es ist durchaus ein sehr großer Unterschied, ob sich 2 Portpins 
gleichzeitig ändern oder nacheinander.

Daher darf hier nichts dahingehend optimiert werden, daß einer erst 
verspätet schaltet.

Z.B. ein SRG (74HC595) würde Dir da sehr böse sein.


Peter

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
zu "volatile": <<This alters the translator to refetch the given 
identifier whenever it encounters an expression that includes the 
identifier. In addition, an object is marked as "volatile" must be 
stored at the point where an assignment to this object takes place.>>

Logischerweise gilt das auch umgekehrt. Wenn genau eine Zuweisung 
dasteht, darf der Compiler nicht zwei daraus machen. Das ANSI-Komitee 
hatte dabei nicht nur Exceptions sondern ausdrücklich auch I/O-Ports im 
Auge. Und bei manchen I/O-Ports macht es nun einmal einen gewaltigen 
Unterschied, ob man ein- oder zweimal reinschreibt (z.B. 
UART-Datenregister).

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Peter: "Ich sach ma, der Ausdruck rechts vom = muß immer zuerst 
berechnet
werden, unabhängig vom volatile."

Nach dieser Definition wäre der Ausdruck
  a[i++] = b[i++];
wohldefiniert. Ist er aber nicht. Weder mit noch ohne "volatile". Eine 
Zuweisung ist kein "sequence point".

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Daher darf hier nichts dahingehend optimiert werden, daß einer erst
> verspätet schaltet.

Ja, genau daher sind die Portadressen ja auch als volatile definiert.
Sonst dürfte der Compiler dort nämlich Dinge zusammenfassen.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@A.K.

ich meinte natürlich nur diese Art Ausdrücke mit Operand=

Bei

c = a + b + c;

ist die Reihenfolge undefiniert.

Aber bei

c += a + b;

muß a + b immmer zuerst berechnet werden.

Der Operand erbt quasi den Rang des = Zeichens.


Peter

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger wrote:

> Aber bei
>
> c += a + b;
>
> muß a + b immmer zuerst berechnet werden.

Peter, deine Erfahrungen in allen Ehren, aber gelegentlich solltest
du dich auch mit dem C-Standard auseinandersetzen.

Nein, a + b muss keinesfalls zuerst berechnet werden.  Der Compiler
hat genauso gut das Recht, obige Anweisungen als

c += a;
c += b;

zu implementieren, solange a, b und c wirklich einfache Variablen
sind, die nicht "volatile" sind.  Diffiziler wird es, wenn Funktionen
im Spiel sind, aber selbst bei

c += a() + b();

darf er das auch zu

c += b();
c += a();

umsortieren, wenn ich mich nicht gerade gründlich irre, da die
Reihenfolge der Bewertung der rechten Seite für den +-Operator nicht
vorgeschrieben ist.  (Andere Operatoren haben eine vorgeschriebene
Reihenfolge, insbesondere ||, && und der Komma-Operator.)  Ach so,
das geht nur so, wenn es keine Chance gibt, dass "c" einen Einfluss
auf die Funktionen a und b hat, also z. B. eine lokale Variable ist.
Bei einer globalen Variablen wäre wohl nur:

tmp = b();
tmp += a();
c += tmp;

zulässig, d. h. jede der Funktionen a und b muss noch den vorherigen
Wert von c sehen.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> wenn ich mich nicht gerade gründlich irre

Du irrst nicht.
In C gibt es ja dieses seltsame Konstrukt des 'sequence
points'. Erst bei Erreichen eines Sequence Points müssen
alle Aktionen abgeschlossen sein. Bis allerdings der
sequence point erreicht ist, hat der Compiler alle
Freiheiten Berechnungen in beliebiger Reihenfolge
auszuführen, solange nur die Operatorreihenfolge
(und die Ausnahmen die Jörg bereits angeführt hat)
gewahrt bleibt.
Ob der Compiler bei einer Zuweisung zuerst die rechte
Seite auswertet (also den zugewiesenen Wert bestimmt)
oder die linke Seite (also die Adressauswertung, wo
der Wert abgespeichert werden soll), ist ganz alleine
ihm überlassen. Der Standard macht da keine Vorschriften,
= ist kein sequence point.

> zulässig, d. h. jede der Funktionen a und b muss noch den vorherigen
> Wert von c sehen.

Das wäre mit allerdings neu, dass sich der C-Standard um sowas
kümmert.
Wenn du mit globalen Variablen in Funktionen arbeitest, dann liegt
es am Programmierer sicherzustellen, dass die Funktionen auch
in der richtigen Reihenfolge aufgerufen werden. Das mit
'muss den vorherigen Wert von c sehen' stimmt also so nicht.

  c = a() +  b()

Wenn es also wichtig ist, dass tatsächlich a() vor b() aufgerufen
wird, dann kann man das nicht so schreiben, sondern muss explizit
eine Reihenfolge erzwingen:

  c = a();
  c += b();

Jetzt liegt zwischen dem Aufruf von a() und b() ein sequence
point, der verhindert, dass der Compiler den Aufruf von b()
vor dem Aufruf von a() machen kann.

> Bei einer globalen Variablen wäre wohl nur:
>
> tmp = b();
> tmp += a();
> c += tmp;
>
> zulässig,

Auch die andere Reihenfolge ist genauso zulässig. Genauso
wie

  c += a();
  c += b();

selbst wenn in a() und in b() c benutzt wird. Das ist das Problem
des Programmierers sich darum zu kümmern. Niemand sagt, dass
bei
  c += a + b;

die +=  Zuweisung eine atomare Einheit sein muss und nicht
auch auf Raten passieren kann. Die einzige Vorgabe die
der Compiler hat: Bei erreichen des ;-sequence points muss
die komplette Aktion abgeschlossen sein.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>> zulässig, d. h. jede der Funktionen a und b muss noch den vorherigen
>> Wert von c sehen.

>Das wäre mit allerdings neu, dass sich der C-Standard um sowas
>kümmert."

Ein Funktionsaufruf ist ein solcher "sequence point" und deshalb kümmert 
sich der Compiler darum. Nicht darum ob erst a() dann b() oder 
andersrum, aber sehr wohl darum, dass c erst nach beiden Aufrufen seinen 
Wert ändert.

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ein Funktionsaufruf ist ein solcher "sequence point" und deshalb
> kümmert sich der Compiler darum.

Wo hatte ich nur meine Gedanken.
War wohl schon spät.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.