Mir kam gerade diese Sonntagsfrage beim Durchsehen meines Sourcecodes für einen Receive-Buffer (der interrupt-gesteuert beschrieben wird) in den Sinn .... Ist es überhaupt sinnvoll bei der Deklaration von statischen Arrays ein >volatile< zu spezifizieren? Da beim Zugriff auf Arrays ja meist ein Index berechnet werden muss kann ein Compiler hier ja keine Voraussagen bzw. Register- Optimierung anwenden um schnelleren Code zu erzeugen, es sei denn man greift mit einem konstanten Index auf ein Array- Element zu. Übersehe ich da auf die Schnelle etwas? Gibt es dazu feste Regeln oder ähnliches? Wirkt ein >volatile< gar nicht bei Arrays? Fragen über Fragen. Vielleicht weiss hier der eine oder andere mehr.
Na ja, vielleicht kann man sich der Sache nähern, indem man den Nutzen von volatile erstmal überhaupt definiert. Also, was soll damit gemacht werden? Als Synchronisationsmittel ist es Quatsch, in fast jedem Fall (höchstens als Funktions-Qualifizierer bei selbstdefinierten classes vielleicht). Wo es wohl angedacht ist, ist wenn sich der referenzierte Inhalt durch Nebenläufigkeiten außerhalb deiner C/C++-Laufzeitumgebung ändern kann. z.B. eine struct auf eine feste Speicheradresse, die z.B. durch SFR-Register geändert wird. Wäre also z.B. ein Pointer auf einen volatile-int, also sowas: volatile int* mySFR = (volatile int*)(0x12345678); Auf der Grundlage könnte eventuell auch ein Array sinnvoll sein. volatile int* mySFR[2] = {(volatile int*)(0x12345678), (volatile int*)(0x12340000)}; Was meiner Meinung nach kaum Sinn machen würde, wäre, das volatile hinter das Sternchen zu schreiben, also so, als ob sich der Pointer ändern würde. Aber prinzipiell könnteste so wohl mySFR[0] und mySFR[1] als Register ansprechen, glaube ich.
Wastl schrieb: > Da beim Zugriff auf Arrays ja meist ein Index berechnet werden > muss kann ein Compiler hier ja keine Voraussagen bzw. Register- > Optimierung anwenden um schnelleren Code zu erzeugen, es sei > denn man greift mit einem konstanten Index auf ein Array- > Element zu. Und wie verhält sich es bei mehrfachem Zugriff, wenn sich der Index zwischendrin nicht ändert?
Es gibt Systeme, die das gleiche RAM an mehreren Stellen des Adressraums einblenden, mit unterschiedlichen Zugriffseigenschaften.
Falk S. schrieb: > indem man den Nutzen > von volatile erstmal überhaupt definiert. Also, was soll damit gemacht > werden? Bezogen auf mein (nicht vorhandenes) "Problem" wäre also die Frage ob mein interrupt-gesteuertes Füllen eines Receive- Buffers irgendwann mal mit dem Auslesen kollidiert, in dem Sinne dass ein Compiler meint hier optimieren zu können, das explizite Lesen aus dem Buffer wäre nicht nötig. Bei einer einzelnen Variablen wäre das ja ein Thema.
Wastl schrieb: > ob mein interrupt-gesteuertes Füllen eines Receive- > Buffers irgendwann mal mit dem Auslesen kollidiert, in dem > Sinne dass ein Compiler meint hier optimieren zu können, Typischerweise ist das Array eines Receiver-Buffers nicht das Problem, da jedes Byte einzeln einmalig gelesen wird. Auch die Indexzeiger brauchen oft kein volatile. Meist brauchst Du volatile nur, wenn eine Variable mehrfach ausgelesen/geschrieben wird UND auf eine Änderung reagiert werden soll bzw. der erneute Zugriff nicht weg optimiert werden darf. Zeig mal (Pseudo-) Code deiner Implementierung.
Man muß bei interruptgesteuerten Sachen beachten, dass, wenn mehrere Variablen gleichzeitig geändert werden, diese untereinander immer einen konsistenten Zustand darstellen. Wenn alle Variablen volatile sind folgt die Variablenänderung strikt dem Programmablauf. Sind die Variablen nicht alle volatile, können Speicherzugriffe umsortiert werden, was die interne Konsistenz verletzt kann. Als Beispiel (für nicht alle volatile) ein Teil meiner UART-Routinen:
1 | static u8 txbuf[16], txw; |
2 | static u8 rxbuf[16], rxr; |
3 | static volatile u8 txr, rxw; |
4 | |
5 | ISR(USART_RX_vect) |
6 | {
|
7 | u8 err = UCSR0A & (B(FE0)|B(DOR0)|B(UPE0)); |
8 | |
9 | rxbuf[rxw] = UDR0; |
10 | if (err == 0) |
11 | {
|
12 | u8 w = (rxw + 1) % sizeof(rxbuf); |
13 | if (w != rxr) |
14 | {
|
15 | rxw = w; |
16 | wakeup("uart_rx"); |
17 | }
|
18 | }
|
19 | }
|
20 | |
21 | ISR(USART_UDRE_vect) |
22 | {
|
23 | if (txr != txw) |
24 | {
|
25 | UDR0 = txbuf[txr]; |
26 | txr = (txr + 1) % sizeof(txbuf); |
27 | wakeup("uart_tx"); |
28 | }
|
29 | else
|
30 | UCSR0B &= ~B(UDRIE0); |
31 | }
|
32 | |
33 | u8 uart_getc(void) |
34 | {
|
35 | sleep_while (rxr == rxw); // sleep while rxbuf empty |
36 | u8 c = rxbuf[rxr]; |
37 | barrier("read rxbuf before changing rxr"); |
38 | rxr = (rxr + 1) % sizeof(rxbuf); |
39 | return c; |
40 | }
|
41 | |
42 | void uart_putc(u8 c) |
43 | {
|
44 | u8 w = (txw + 1) % sizeof(txbuf); |
45 | |
46 | sleep_while (w == txr); // sleep while txbuf full |
47 | txbuf[txw] = c; |
48 | barrier("write txbuf before changing txw"); |
49 | txw = w; |
50 | UCSR0B |= B(UDRIE0); |
51 | }
|
[Das wakeup-Makro kann man ignorieren, das sleep_while als while betrachen (die ermöglichen, dass der Prozessor in den sleep-Modus geht).] Es gibt nur zwei volatile Variablen (txr und rxw), die Buffer selbst sind nicht volatile. Das barrier-Makro[1] ist eine memory barrier, die dem Compiler verbietet, Speicherzugriffe über dieses Makro hinweg zu verschieben. Das Argument von barrier dient ausschließlich der Kommentierung und zeigt hier, was verhindert werden soll, damit die Daten (Buffer ind read-/write-Indizes) konsistent bleiben. Klar, man könnte auch alles volatile machen oder atomic-blocks benutzen, aber ich finde es so eleganter :-) [1] FYI: #define barrier(note) __asm volatile("":::"memory")
Ich habe mit Programmen auf PCs am meisten Erfahrung. Wenn man auf dem PC mit Visual Studio (oder gcc ...) die Optimierung angeschaltet hat, macht der Compiler viele Dinge. Er kann z.B. ganze Funktionsaufrufe, die zur Compile-Zeit schon berechnet werden können, ausrechnen. Im Code steht dann nur noch das Ergebnis als Konstante. (Siehe Anlage). Dort hat der Compiler nicht nur das ganze Array wegoptimiert, sonder die Suchfunktion gleich mit. Wenn man das Array als volatile klassifiziert, dann macht er das nicht. Also: Wann immer sich die Daten außerhalb des eigenen Programmes/Threads ändern können, sollte man das Array als volatile deklarieren. Die Verwendung ist aber nicht beliebt, weil der Compiler dann eben nicht gut optimieren kann, ... Gruß
Georg W. schrieb: > Die Verwendung ist aber nicht beliebt, weil der Compiler dann eben nicht > gut optimieren kann, ... Die Verwendung von Sprachkonstrukten in Programmiersprachen, besonders die Verwendung von volatile, sollte man niemals von irgend einer Beliebtheit abhängig machen. Oliver
"Die Verwendung von Sprachkonstrukten in Programmiersprachen, besonders die Verwendung von volatile, sollte man niemals von irgend einer Beliebtheit abhängig machen." Hatte ich wohl unglücklich ausgedrückt. Gemeint ist: Wenn das volatile nicht wirklich benötigt wird, sollte man es weglassen, aber eben nur dann.
Georg W. schrieb: > Hatte ich wohl unglücklich ausgedrückt. Du hast auch "unglücklich" zitiert. Nachdem du schon mehr als acht Jahre hier angemeldet bist solltest du langsam auf den Trichter kommen wie man hier üblicherweise kinderleicht Texte/Beiträge zitiert.
Georg W. schrieb: > Wann immer sich die Daten außerhalb des eigenen Programmes/Threads > ändern können, sollte man das Array als volatile deklarieren. Ich wüsste nicht, wo man volatile im Kontext von Threads sinnvoll nutzen könnte, oder wo die ganze Fragestellung, mit der sich volatile befasst, bei Userspace-PC-Programmierung überhaupt auftritt. Der Kern-Anwendungsfall von volatile sind Adressen, die kein RAM sind, also z.B. Register auf einem Mikrocontroller. 99.9% aller Fälle, wo ein volatile qualifier an irgendwas dransteht was keine Registeradresse auf einem Mikrocontroller sind, sind Unsinn.
Sven B. schrieb: > Ich wüsste nicht, wo man volatile im Kontext von Threads sinnvoll nutzen > könnte Die kreative Nutzung von volatile für alles Mögliche und Unmögliche im Zusammenhang mit der Synchronisation von Daten zwischen Threads u.ä. hatte das C++-Standardization Commitee dazu gebracht, zukünftig so ziemlich alle Operationen auf volatile Variablen zukünftig zu verbieten. Zum Glück haben die das inzwischen zurückgenommen, weil damit auch die eigentliche Verwendung kaum noch möglich gewesen wäre. Es wird da draußen tonnenweise Code geben, in denen volatile fälschlicherweise genutzt wird. Oliver
:
Bearbeitet durch User
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.