Technologie
Menschen Wissenschaft Politik Mystery Kriminalfälle Spiritualität Verschwörungen Technologie Ufologie Natur Umfragen Unterhaltung
weitere Rubriken
PhilosophieTräumeOrteEsoterikLiteraturAstronomieHelpdeskGruppenGamingFilmeMusikClashVerbesserungenAllmysteryEnglish
Diskussions-Übersichten
BesuchtTeilgenommenAlleNeueGeschlossenLesenswertSchlüsselwörter
Schiebe oft benutzte Tabs in die Navigationsleiste (zurücksetzen).

C/C++ Thread

85 Beiträge ▪ Schlüsselwörter: Programmierung, C/c++ ▪ Abonnieren: Feed E-Mail

C/C++ Thread

22.08.2013 um 10:40
@McNeal
Zitat von McNealMcNeal schrieb:Nie nie nie nie niemals goto verwenden.
Na das stimmt so nun auch wieder nicht, es gibt ein paar praktische Fälle, wo das absolut sinnvoll ist:

void copy_datei(von, nach) {

if (!oeffne_datei(von))
return;

if (!oeffne_datei(nach))
goto aufraeumen;

copy(von, nach);

schliesse_datei(nach);

aufraeumen:
schliesse_datei(von);
}

Ohne goto hätte man hier verschachtelte if's, die sehr tief werden können, wenn noch mehr Abhängigkeiten im Programm auftreten. Hier war es ja nur eine.

Der Linux-Kernel ist übrigens voll mit gotos.

Zäld

Anzeige
melden

C/C++ Thread

22.08.2013 um 10:57
Nein.. Nein Nein, biite kein goto sondern sowas hier:

void copy_datei(von, nach) {

if (oeffne_datei(von)) {
if (oeffne_datei(nach)) {
copy(von, nach);
schliesse_datei(nach);
}
schliesse_datei(von);
}
}


1x zitiertmelden

C/C++ Thread

22.08.2013 um 11:42
@zaeld

Ich braeuchte jetzt so ein Morpheus-meme mit 'What if I told you that there are more possibilities than convoluted if statements' :)

Wie waers denn mit ner stream Klasse, die offene Files selbst schliesst oder einfach ne Funktion die genau das kapselt? Goto ist und bleibt dreckig und unnoetig.
Der Linuxkernel ist halt wie sovieles irgendwo zwischen 70er und 80er Jahren haengengeblieben.

Uebrigens kriegst du die Abhaengigkeiten mit Goto auch nicht weg, du ersetzt damit nur {}-Bloecke mit Label-Bloecken, die dank goto noch viel komplexer ineinandergreifen koennen als es mit {} je moeglich waere.


1x zitiertmelden

C/C++ Thread

22.08.2013 um 11:55
@McNeal In C++ sieht die Sache noch mal etwas anders aus als in C, wenn man Einfluss auf die genaue Gestaltung des Maschinencodes nehmen will, z.B. in Kernelprogrammierung. Klassen hat man dort sowieso nicht, aber manchmal will man schon Funktionsaufrufe vermeiden.


melden

C/C++ Thread

22.08.2013 um 12:08
@RaChXa

Naja die 10 Zeilen auf die es ankommt kann man dann direkt in Assembler schreiben. Und Makros gibts schliesslich auch noch wenn man partout keine Funktionen aufrufen will. Mag schon sein dass es irgendwo ein oder zwei Beispiele gibt wo man mit goto noch was rausholen koennte aber empfehlenswert ist es deshalb auf keinen Fall schon allein wegen der Unuebersichtlichkeit.


melden

C/C++ Thread

22.08.2013 um 18:27
Zitat von subgeniussubgenius schrieb:Nein.. Nein Nein, biite kein goto sondern sowas hier:

void copy_datei(von, nach) {

if (oeffne_datei(von)) {
if (oeffne_datei(nach)) {
copy(von, nach);
schliesse_datei(nach);
}
schliesse_datei(von);
}
}
Und wenn man dann noch mehr Abhängigkeiten hat (im Sinne von "tu dies erst, wenn das hier erfolgreich war"), verschachtelt sich das immer weiter und es wird total unübersichtlich.

Es gibt auch noch einen anderen Fall, nach dem Muster:

void lese_datei() {

reserviere_speicher();
blockiere_datei();
oeffne_datei();

if (!lese_tabellenname_von_datei())
goto out;

if (!lese_zeilenanzahl_von_datei())
goto out;

for (i = 0 ... zeilenanzahl) {

if (!lese_spaltenanzahl_von_datei())
goto out;

for (j = 0 ... spaltenanzahl) {

if (!lese_element_von_datei())
goto out;

}
}

out:
schliesse_datei();
datei_freigeben();
speicher_freigeben();

}

Und da wird das ohne goto schon deutlich unübersichtlicher.
Zitat von McNealMcNeal schrieb:Wie waers denn mit ner stream Klasse, die offene Files selbst schliesst oder einfach ne Funktion die genau das kapselt? Goto ist und bleibt dreckig und unnoetig.
Klar kann man das alles ohne goto irgendwie hinbiegen, aber einen Teil in eine Funktion auszulagern erschwert aber den Lesefluß und das Verständnis des Programms.

Solange man goto sinnvoll verwendet und nicht etwa Schleifen oder ähnliches damit realisiert, ist das auch nicht dreckig, nur weil mal ein Professor davor gewarnt hat. Man sollte nur wissen, was man da tut.

Zäld


1x zitiertmelden

C/C++ Thread

22.08.2013 um 19:19
@zaeld
Mit den linux kernel-Gurus will ich nicht streiten ;) ..dpch ich als Anwendungsentwickler würde es niemals wagen ein goto abzuliefern..und ich würde auch keines akzeptieren (Wenns nicht vom Boss ist)

Switch- cases können ein weitere Möglichkeit sein ..

Wenn es um High-Level Funktionen geht wie ein Dateizugriff gehört in C++ ein try catch Block rum.
Über die Performane von try & catch muss nicht diskutiert werden, denn Funktionen wie Dateizugriffe rufen (minimal) eine OS Funktion auf und sowas dauert mehrere ms.


Zu deinen Beispeil ... du hattest besser den Funtionen keinen Sprechenden Namen gegeben, warum liest du die Spaltenzahl der Datei wiederholt aus ?

wie wäre es damit ?

void lese_datei() {

reserviere_speicher();
blockiere_datei();
oeffne_datei();

if (lese_tabellenname_von_datei() && lese_zeilenanzahl_von_datei() && lese_spaltenanzahl_von_datei()) {
for (i = 0 ... zeilenanzahl*spaltenzahl) {
if (!lese_element_von_datei())
break;
}
}

schliesse_datei();
datei_freigeben();
speicher_freigeben();
}


1x zitiertmelden

C/C++ Thread

22.08.2013 um 20:09
Zitat von subgeniussubgenius schrieb:Mit den linux kernel-Gurus will ich nicht streiten
In der Richtlinie zum Linux Coding Style sind gotos explizit behandelt und als sinnvoll erlaubt. In bestimmten Fällen.
Zitat von subgeniussubgenius schrieb:dpch ich als Anwendungsentwickler würde es niemals wagen ein goto abzuliefern..und ich würde auch keines akzeptieren
Ich schätze mal nur deswegen, weil das alle eben so sagen, oder? So ist oder war es zumindest bei mir. Einen sachlichen Grund gegen den wohldosierten Einsatz gibt es eigentlich nicht, ich bin auch erst durch den Kernel darauf gekommen, daß ein goto durchaus auch mal die eleganteste Lösung sein kann (und da gehört das Programm, das die goto-Diskussion gestartet hat, ganz bestimmt nicht dazu).
Zitat von subgeniussubgenius schrieb:du hattest besser den Funtionen keinen Sprechenden Namen gegeben, warum liest du die Spaltenzahl der Datei wiederholt aus ?
Na, dann sage ich eben, daß jede Zeile eine andere Anzahl von Spalten haben kann. Mit anderen Worten daß zu jedem Eintrag eine unterschiedliche Anzahl von Parametern zugeordnet ist. Gut, dann passen die Namen der Funktionen tatsächlich nicht vollständig, aber es geht ja auch nur ums Prinzip.
Zitat von subgeniussubgenius schrieb:wie wäre es damit ?
Wenn ich bei jedem Einlesen eine genaue Fehlermeldung ausgeben möchte, an welcher Stelle es genau hakte, funktioniert das schon wieder nicht.

Und es war ja auch nur als Beispiel gedacht, ganz allgemein kann man die Aufgaben eben nicht so einfach zusammenfassen.

Zäld


melden

C/C++ Thread

22.08.2013 um 20:25
@zaeld
Wie ich schon sagte .. ich werde nicht mit den Kernelgurus streiten.
Dennoch kein goto ausserhalb des linux kernels weil wir es ALLE sagen. Es ist Konvention und daher schlechtes Benehmen wenn du davon abweichst.
Was du in deinen privaten closedsource Projekten machst ist deine Sache, aber wenn du willst das jemand Anderes mit an deinen Code arbeitet dann halte dich bitte an die NO GOTO KONVENTION.

Linus & Co wissen was sie tuhen .. die meisten Entwickler müssen aber erzogen werden. Erst wenn du ein Jedimeister der Alghorithmen bist darfst tu GOTO anwenden .. und jeden vertäufeln der es auch tut.


Hier was zum entspannen im Job: http://devopsreactions.tumblr.com/


melden

C/C++ Thread

22.08.2013 um 21:54
@kosinus0815

Ich sehe übrigens gerade die Initialisierung von b mit 0 ist natürlich überflüssig

Und:
Zitat von McNealMcNeal schrieb:while(a >= 10 )
{
a /= b;
}
Gehr sogar noch knapper ;) :

while( (a /=b) >= 10);


Da die Zuweisung mit = selbst auch das Ergebnis der Zuweisung liefert, kann man darauf eine Vergleichsoperation anwenden und alles in den Schleifenkopf schreiben, mit leeren Anweisungsblock


melden

C/C++ Thread

22.08.2013 um 22:11
@zaeld
gut, sagen wirs mals so: In c kann man solche Aufraeum/Exception-Gotos noch irgendwo durchgehen lassen aber in c++ gibts dafuer wirklich bessere Mittel. Wobei selbst in c kann man das in ein Aufraeum-Makro packen und man muss lediglich das goto durch dieses Makro ersetzen.
Zitat von zaeldzaeld schrieb:Klar kann man das alles ohne goto irgendwie hinbiegen, aber einen Teil in eine Funktion auszulagern erschwert aber den Lesefluß und das Verständnis des Programms.
Hast du mir ein Beispiel dafuer, wo das den Lesefluss/das Verstaendnis behindert?


melden

C/C++ Thread

22.08.2013 um 23:20
Um mal das Thema weiterzuentwickeln..

Für Alghorithmen mit langläufigen Schleifen sollte man unbedingt bedingte Sprünge vermeiden.
Bedingte Sprünge belasten die Prozessorpipeline. Die Compiler sind nicht so creativ wie es es der Entwickler sein kann.

Das hier ...
/** Aufwärmen **/
for (int i=0; i<param.get_Warmup(); ++i)
{
for (int j=0; j < param.get_SeqSize();++j) {
f*= (1-f)*param.get_Seq(j)?a:b;
}
}

läuft fast 20% schneller.. wenn so :
const float ab[2] = {a,b};

/** Aufwärmen **/
for (int i=0; i<param.get_Warmup(); ++i)
{
for (int j=0; j < param.get_SeqSize();++j) {
f*= (1-f)*ab[param.get_Seq(j)];
}
}


melden

C/C++ Thread

22.08.2013 um 23:38
@subgenius

Liegt vielleicht auch daran, dass der 2. Code was unterschiedliches tut :)


melden

C/C++ Thread

22.08.2013 um 23:41
@McNeal
nö.. wieso ?


PS: dass allmy Board schluckt einen Teil des Codes ..


melden

C/C++ Thread

22.08.2013 um 23:46
@subgenius

1. html tags ;-)
2. ich wuerde sagen (1-f)*param.get_Seq(j) wird zuerst ausgewertet und dann fuer den ?-operator verwendet weil der ?-operator eine sehr niedrige Prioritaet hat. Zumindest wuerde ich klammern.


melden

C/C++ Thread

22.08.2013 um 23:53
Nein.. der Operator wird schon richtig ausgewertet
? hat höhere Bindung als *

Der Unterschied macht wirklich das kleine ab array aus.

Hat was mit der Reihenfolge des Codes in der Prozessorpiline zutuhn.
Die ab array Variante enthält einen bedingten Sprung weniger der die Reihenfolge der Instruktionen des Codes in der Befehlspipline komprementiert.


melden

C/C++ Thread

23.08.2013 um 00:06
@subgenius

http://en.cppreference.com/w/cpp/language/operator_precedence

* Prioritaet 5
?: Prioritaet 15

Grad eben getestet um sicherzugehen und siehe da, die Ergebnisse waren unterschiedlich.

Die Aussage man sollte bedingte Spruenge unbedingt vermeiden find ich auch schon fragwuerdig. Zum einen kriegen moderen Compiler doch schon echt krasse Optimierungen hin, die manuelle Optimierung uebertreffen koennen, und zum anderen lohnt es im Allgemeinen nicht alles zu optimieren. Lieber ordentlich und leserlich schreiben und wirklich nur die bottlenecks optimieren, die dann aber richtig :)


1x zitiertmelden

C/C++ Thread

23.08.2013 um 00:16
@McNeal
Nun.. dann werden wohl klammern drumgewesen sein.
Ich finde das artefakt nicht mehr ..

Begründung stimmt dennoch.

Auch was SIMD angeht kann man dem gcc auf die sprünge helfen.
Die selbe schleife kann auch einmal für 4 verschidene a,b paramter durchlaufen werden.. der gcc optimiert sowas nicht von alleine.


v4sf f=v4sfl(param.get_StartValue()); // Lade 4 mal die startwerte in den floatvector f
const v4sf ab[2]={a,b};
const v4sf one=v4sfl(1.0); // Lade 4 mal die 1 in den floatvector one
/** Aufwärmen **/
for (int i=0; i<param.get_Warmup(); ++i)
{
for (int j=0; j < param.get_SeqSize();++j) {
f*=(one-f)*ab[param.get_Seq(j)];
}
}


melden

C/C++ Thread

23.08.2013 um 00:22
Zitat von McNealMcNeal schrieb:Die Aussage man sollte bedingte Spruenge unbedingt vermeiden find ich auch schon fragwuerdig. Zum einen kriegen moderen Compiler doch schon echt krasse Optimierungen hin, die manuelle Optimierung uebertreffen koennen, und zum anderen lohnt es im Allgemeinen nicht alles zu optimieren. Lieber ordentlich und leserlich schreiben und wirklich nur die bottlenecks optimieren, die dann aber richtig :)
Es sind gerade die großen CPU optimierungen die hier den Unterschied machen.
Der Prozessor sortiert instruktionen und Daten in unterschiedlichen Ebenen Piiplines und Caches.
piplines sind die schnellesten und letzten Speicherinstanzen von der eigentlichen Ausführung und die sind unbedingt.
Wenn also die von der Sprungvorhergesagte Reihenfolge nicht stimmt dann muss aus dem L1 Cache die nächste instruktion angefordert werden .. das bedeutet Warten.

Warum der gcc das nicht besser kann .. das weis ich nicht.


melden

C/C++ Thread

23.08.2013 um 00:27
@McNeal
@subgenius

Also

get_Seq(j)?a:b;

und

ab[param.get_Seq(j)];

sind schon äquivalent.

Ich vermute, wenn a und b aufwendig berechnet werden müssen macht die erste Variante Sinn, weil dann abhängig vom Wahrheitswert entweder a oder b berechnet wird.
In der Zweiten Variante müssen sowohl a als auch b berechnet werden, um sie in das Array zu schreiben, dafür allerdings muss danach bloß, abhängig vom Wahrheitswert eine Variable ausgelesen werden und kein Sprung ausgeführt werden.

Aber warum sollte der Compiler das nicht optimieren können?


1x zitiertmelden

C/C++ Thread

23.08.2013 um 00:36
@subgenius

Und selbst wenn das doppelt so schnell ist sollte man nicht jedes Kinkerlitzchen optimieren. Das macht den Code unleserlich und fehleranfaellig. Wenn du ne Numerikbibliothek schreibst und dort das Skalarprodukt optimierst, dann macht das durchaus Sinn. Aber eben nur in der innersten Schleife wo der Grossteil der Zeit verbraucht wird und nicht im Rest des Programms, das von mir aus ne kleine Config Datei laedt oder sowas. Optimierung ist zweitrangig.

Ich weiss, ich weiss, diese Art der Optimierung macht Spass weil man direkt belohnt wird und es vergleichsweise einfach ist. Ich mach das auch total gerne, jedoch sollte mans nicht uebertreiben und nie Sachen optimieren, die keine Bottlenecks sind.

@RaChXa

Es ging darum, dass (1-f) * param.get_Seq(j) zuerst ausgewertet wird und damit etwas anderes passiert. Mit dem Code hier:

f*= (1-f)*param.get_Seq(j)?a:b;

passiert sozusagen das:
f*= ((1-f)*param.get_Seq(j))?a:b;

wobei man eigentlich das hier haben will:

f*= (1-f)*(param.get_Seq(j)?a:b);


melden

C/C++ Thread

23.08.2013 um 00:46
@McNeal

Achso, ja das stimmt:
http://de.wikibooks.org/wiki/C-Programmierung:_Liste_der_Operatoren_nach_Priorit%C3%A4t

Edit: Ach hast oben auch schon eine Tabelle gepostet, gerade gesehen.


@subgenius
Zitat von RaChXaRaChXa schrieb:Ich vermute, wenn a und b aufwendig berechnet
Ich meine damit, ersetze a und b durch f(a) und f(b) und die Sache mit dem Geschwindigkeitsvorteil sieht anders aus.


melden

C/C++ Thread

23.08.2013 um 10:39
@McNeal
Das Stück ist aus einer Function zur Berechnung eines Bildpunktes für ein Ljapunov diagramm. Da wird nicht irgendetwas Optimiert sondern der Ablauf des zentralen Alghorithmus in dem 99.99% der Rechenzeit verbraten wird.

Um das Teil zum rennen zubekommen, verwendet ich am L1 &L2 Cache ausgerichtete Datenstrukturen um 'cache miss' Zugriffe zu minimieren, SIMD Datenstrukturen (SSE) und Multithreading.


Das mit dem ?: tut mir Leid, wäre mir bei der Laufzeit schon aufgefallen ;)

320px-Zirconzity
Wikipedia: Ljapunow-Diagramm

@RaChXa
A und B sind Floatwerte aus einen gegeben Intervall die einmal pro Bildpunkt über die X, Y Coordinaten ermittelt werden.

param.get_Seq(j) liefert eine Sequenz von Boolschen werten. Da die Sequenz erst zur Laufzeit festeht kann der Compiler den Code nicht mit Bestimmtheit in der Reihenfolge sortieren wie er dann ausgeführt wird. In der zweiten Variante wird aber kein Sprungerzeugt sondern eine Adresse für den Parmeter der Funktion berechnet (ab[0] oder ab[1]).. und mehr wollen wir nicht haben.

Das Thema Sprungelimination scheint recht schwieriges für den Compiler zusein.
Ein Ansatz sind Super Optimierer die einfach blind Codesequenzen suchen die sich wie gewünscht verhalten und wählen dann die schnellste Variante.


Relevantes dazu:
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.58.3509&rep=rep1&type=pdf
http://www.mathematik.uni-ulm.de/sai/ws10/cpp/cpp-2011-01-31.pdf


1x zitiertmelden

C/C++ Thread

23.08.2013 um 12:03
@subgenius
Zitat von subgeniussubgenius schrieb:param.get_Seq(j) liefert eine Sequenz von Boolschen werten. Da die Sequenz erst zur Laufzeit festeht kann der Compiler den Code nicht mit Bestimmtheit in der Reihenfolge sortieren wie er dann ausgeführt wird.
Das muss er auch nicht.
Der Compiler kann den Maschinencode so gestallten dass a und b in den Speicher geschrieben werden. Und dann abhängig vom booleschen Wert die entsprechende Adresse auslesen. Dafür brauch man keinen Sprung sondern die booleschen Werte werden mit der passenden Speicheradresse assoziiert. Eben genau das was du manuell mit den Array gemacht hast. Man sollte doch meinen dass der Compiler das hinbekommt. Ich habe es allerdings nicht getestet. Kann gut sein dass du recht hast. Mit welchen Compilern hast du es getestet?
Zitat von subgeniussubgenius schrieb:In der zweiten Variante wird aber kein Sprungerzeugt sondern eine Adresse für den Parmeter der Funktion berechnet (ab[0] oder ab[1]).. und mehr wollen wir nicht haben.
Ja ich weiß, das schrieb ich ja. Aber wie gesagt dafür müssen erstmal sowohl a als auch b berechnet werden um sie in das Array schreiben zu können.

Betrachte
ab[0] = f(a); ab[1] = g(b); r = ab[x];
gegen
r = x?f(a):g(b);

(wobei x boolscher Wert)


f und g müssen hier auch gar keine Funktionsaufrufe sein. Sondern können auch als Pseudocode aufgefasst werden, wobei f,b Funktionen im mathematischen Sinne sein können oder komplexe Ausdrücke in Abhängigkeit von a bzw. b

Und in der Zweiten Variante müsste man nicht erst f(a) und g(b) berechnen sondern, entweder f(a) oder g(b)


1x zitiertmelden

C/C++ Thread

23.08.2013 um 12:10
Edit: sollt natürlich heißen:
wobei f,g Funktionen im mathematischen ...


Anzeige

melden