Forum: PC-Programmierung Direkt-Initialisierung in ctor schlechter optimierbar als indirekt?


von Verschwindibus (Gast)


Lesenswert?

Einen wunderschönen guten Abend!

Ich bin auf ein unintuitives Problemchen gestoßen..
Ich habe 2 Möglichkeiten für einen cast-ctor:
1
//Fall 1:
2
template<typename U>
3
X( U&& u) : X_b( std::forward<U>(u) ){
4
}
5
6
//Fall 2:
7
template<typename U>
8
X( U&& u){
9
  X_b xb( std::forward<U>(u) );
10
  (*this) = xb;
11
}

Hat jemand eine Idee, warum das generierte ASM im Fall 2 besser 
optimiert ist, als im Fall 1? Der Compiler scheint im ersten Fall extra 
eine Ctor-Funktion zu erstellen und zu benutzen. Im zweiten Fall tauch 
die jedoch nicht auf.

Woran könnte das liegen??

MfG

von Oliver S. (oliverso)


Lesenswert?

Verschwindibus schrieb:
> Hat jemand eine Idee, warum das generierte ASM im Fall 2 besser
> optimiert ist, als im Fall 1? Der Compiler scheint im ersten Fall extra
> eine Ctor-Funktion zu erstellen und zu benutzen.

Bei mir nicht...

Der Compiler erzeugt aus deinem Codeschnipsel zunächst mal gar nichts. 
Da kommt es wohl darauf an, was du mit den Templates dann tatsächlich 
anstellst.

Daher wäre ein compilierbares Codebeispiel hilfreich, und dazu eine 
Aussage, welcher Compiler den Effekt zeigt.

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Verschwindibus schrieb:
> Einen wunderschönen guten Abend!
>
> Ich bin auf ein unintuitives Problemchen gestoßen..
> Ich habe 2 Möglichkeiten für einen cast-ctor:
>
>
1
> //Fall 1:
2
> template<typename U>
3
> X( U&& u) : X_b( std::forward<U>(u) ){
4
> }
5
> 
6
> //Fall 2:
7
> template<typename U>
8
> X( U&& u){
9
>   X_b xb( std::forward<U>(u) );
10
>   (*this) = xb;
11
> }
12
>
>
> Hat jemand eine Idee, warum das generierte ASM im Fall 2 besser
> optimiert ist, als im Fall 1? Der Compiler scheint im ersten Fall extra
> eine Ctor-Funktion zu erstellen und zu benutzen. Im zweiten Fall tauch
> die jedoch nicht auf.
>
> Woran könnte das liegen??

Fall2 dürfte wohl gar nicht kompilieren, weil X_b wohl der Namen eines 
Attributs der umgebenden Klasse sein soll. Dort wird es aber als Typ 
verwendet. Dies bedeutet dann auch, dass Du den Fall 2 niemals 
instanziiert hast (also Funktionstemplate des ctors), denn sonst wäre 
der Fehler wohl aufgefallen.

Also: poste Dein komplettes Beispiel.

von Verschwindibus (Gast)


Lesenswert?

Hallo,
Danke für die Antworten trotz der mageren Informationslage..
Ich habe mir das Ganze noch mal genauer angeschaut und festgestellt, 
dass es sich im ASM nicht um einen un-ge-inlined-en Ctor handelt, 
sondern ein Callback.. Und der Grund dafür schein ein uninitialisiertes 
Array gewesen zu sein.

Kann das sein? Kommt das jemandem bekannt vor, oder ergibt das sogar 
Sinn?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Verschwindibus schrieb:
> Kann das sein? Kommt das jemandem bekannt vor, oder ergibt das sogar
> Sinn?

Ohne konkreten Code, nein. Ist ziemlich undurchsichtig so. Was für ein 
Callback? C++ erzeugt nicht ungefragt irgendwo Callbacks...

von Verschwindibus (Gast)


Lesenswert?

Die Verwirrung geht weiter:
Ich habe hier mal ein entsprechendes Codeschnipsel zusammengemalt:
1
template<typename T>
2
struct X : X_b<T> {
3
4
  using X_b<T>::X_b; // mit X_b<T>::X_b() : existiert, ist aber leer
5
6
7
  constexpr vectLE( const Y & y)
8
    : X<T>()                        // A
9
    : X_b<T>()                        // B
10
    : X<T>( [&](bool b){ return y.g(b); } )          // C
11
    : X_b<T>( [&](bool b){ return y.g(b); } )        // D
12
  {
13
    this->initWieCtor( [&](bool b){ return y.g(b); } );   // E
14
  }
15
16
  // A + E   ("default + verzögert")      : optimiert..
17
  // B + E   ("basis default + verzögert")  : nicht optimiert.. // warum nicht wie "A + E" ?
18
  // E    ("verzögert")          : nicht optimiert..  // ?????
19
  // C     ("direkt")            : nicht optimiert..
20
  // D     ("basis direkt")        : nicht optimiert..
21
  // C/D + E  ("direkt + verzögert")      : optimiert..    // ?????
22
23
};

von Oliver S. (oliverso)


Lesenswert?

Was soll das denn jetzt werden? Eine Ansammlung sinnloser 
Buchstabenfolgen?

Zeig compilierbaren Code, alles andere ist völlig sinnlos.

Oliver

von Verschwindibus (Gast)


Lesenswert?

Ps.. Kann eigentlich doch nicht an uninitialisierten Daten gelegen 
haben, da mein array zumindest als ": array{}" im ctor aufgetaucht ist 
(und somit dessen elemente default-initialisiert werden, wenn ich das 
richtig sehe). Und außerdem, weil im Codeschnipsel-Bsp. eben gar kein 
member in irgendeinem "ctor()" initialisiert wird.. ( im "ctor(fkt)" 
natürlich schon).

Mit Callback meinte ich g().

von Verschwindibus (Gast)


Lesenswert?

@Oliver: Ja, natürlich soll es genau das werden...
Oder, ich hatte erwartet, dass der geneigte Leser es ohne groß weitere 
Worte versteht:
Man nehme, für ein entsprechendes Bsp des Problems eine Kombination der, 
jeweils mit A, B, C, D, oder E gekennzeichneten, Zeilen, entsprechend 
der unten stehenden, zugehörigen 'Rezeptur' des jew Beispiels.
Außerdem seien in X_b, wie erwartet, natürlich auch initialisierbare 
(POD) Daten vorhanden.

von Oliver S. (oliverso)


Lesenswert?

Verschwindibus schrieb:
> Man nehme

Was genau verstehst du an "kompilierbarem Beispiel" nicht? Zeig ein 
vollständiges Programm, mit allem, was dazugehört. Wie sonst soll man 
deine Aussagen zum erzeugten Assemblercode überprüfen können?

Die Frage nach Compiler/Toolchain hast du auch noch nicht beantwortet.

Oliver

von Verschwindibus (Gast)


Lesenswert?

>Was genau verstehst du an "kompilierbarem Beispiel" nicht? Zeig ein
>vollständiges Programm, mit allem, was dazugehört. Wie sonst soll man
>deine Aussagen zum erzeugten Assemblercode überprüfen können?

Wie wärs mit dem erzeugten ASM-Code? Dann musst du dich nicht weiter auf 
un-realisierbaren Forderungen versteifen..

Als würde das mal eben so gehen. Wäre das so einfach, würde ich nicht 
nach Mutmaßungen Ausschau halten.

Mit anderen Worten, jemand, der mir helfen kann, sollte jetzt schon mehr 
parat haben, als dumme Sprüche und Geweine.

Ich arbeite mit GCC, aber in der Hoffnung, dass es nichts 
Compiler-abhängiges ist.

von Oliver S. (oliverso)


Lesenswert?

Verschwindibus schrieb:
> //Fall 2:
> template<typename U>
> X( U&& u){
>  X_b xb( std::forward<U>(u) );
>   (*this) = xb;
> }

Der Code lässt mich halt etwas zweifeln, ob du da überhaupt weißt, was 
du da tust.

Oliver

: Bearbeitet durch User
von Kaj (Gast)


Lesenswert?

Verschwindibus schrieb:
> un-realisierbaren Forderungen
Was ist an einem compilierbaren Beispiel, welches das Problem aufzeigt, 
unrealisierbar?

von Verschwindibus (Gast)


Lesenswert?

>Der Code lässt mich halt etwas zweifeln, ob du da überhaupt weißt, was
>du da tust.
>
>Oliver

Man merkt dir deine Verunsicherung an. Wenn du häufiger 
Misstrauensprobleme hast, rate ich dir, einen eigenen Thread 
aufzumachen. Auch wenn du in einem Psycho-Forum besser damit aufgehoben 
wärst, Oliver.

Andernfalls gäbe es ja noch Inhalte, wie weitere Codeschnipsel und Text, 
auf die du dich beziehen könntest... Vorausgesetzt du fühlst dich in 
beiden Fällen angesprochen.*


*
>Mit anderen Worten, jemand, der mir helfen kann, sollte jetzt schon mehr
>parat haben, als dumme Sprüche und Geweine.


@Kaj:
Die Komplexität. Was nützt ein kompilierbares Beispiel, wenn es das 
Problem evtl nicht mehr enthält?

Ein, das Problem enthaltendes, Beispiel, für ein Problem unbekannten 
Ursprungs zu fordern ist schon genug Hirnakrobatik für sich genommen..

von Rolf M. (rmagnus)


Lesenswert?

Verschwindibus schrieb:
> @Kaj:
> Die Komplexität. Was nützt ein kompilierbares Beispiel, wenn es das
> Problem evtl nicht mehr enthält?

Hmpf. Du behauptest, dass irgendein GCC da anderen Code generiert als du 
erwartest. Wenn es unmöglich ist, ein compilierbares Beispiel daraus zu 
machen, ohne dass der Fehler verschwindet, wie soll sich da jemand den 
generierten Code anschauen können? Und wie hast du das dann gemacht?

> Ein, das Problem enthaltendes, Beispiel, für ein Problem unbekannten
> Ursprungs zu fordern ist schon genug Hirnakrobatik für sich genommen..

Der erste Schritt, um ein Problem anzugehen, ist immer der Versuch, es 
zu reproduzieren. Wenn man das nicht kann, hat man eigentlich keine 
Chance, außer es einfach hinzunehmen.

von Gcc (Gast)


Lesenswert?

Lieber TO,
der GCC erzeugt in allen Fällen genau den selben Code:
exakt 0 Byte.

Da stehen nämlich nur Templates und diese werden nie konkretisiert.
Die Frage hätte auch ein unbenutztes Macro zeigen können. Solange 
Template oder Macro nie benutzt werden, sind sie wie ein Beipackzettel 
den keiner liest.

von Wilhelm M. (wimalopaan)


Lesenswert?

Verschwindibus schrieb:
> Die Verwirrung geht weiter:
> Ich habe hier mal ein entsprechendes Codeschnipsel zusammengemalt:
>
>
1
> template<typename T>
2
> struct X : X_b<T> {
3
> 
4
>   using X_b<T>::X_b; // mit X_b<T>::X_b() : existiert, ist aber leer
5
> 
6
> 
7
>   constexpr vectLE( const Y & y)
8
>     : X<T>()                        // A
9
>     : X_b<T>()                        // B
10
>     : X<T>( [&](bool b){ return y.g(b); } )          // C
11
>     : X_b<T>( [&](bool b){ return y.g(b); } )        // D
12
>   {
13
>     this->initWieCtor( [&](bool b){ return y.g(b); } );   // E
14
>   }
15
> 
16
>   // A + E   ("default + verzögert")      : optimiert..
17
>   // B + E   ("basis default + verzögert")  : nicht optimiert.. // warum 
18
> nicht wie "A + E" ?
19
>   // E    ("verzögert")          : nicht optimiert..  // ?????
20
>   // C     ("direkt")            : nicht optimiert..
21
>   // D     ("basis direkt")        : nicht optimiert..
22
>   // C/D + E  ("direkt + verzögert")      : optimiert..    // ?????
23
> 
24
> };
25
>

Was soll das sein? Zudem ganz anders als Dein Eingangsproblem.

Wo ist das Basisklassentemplate X<>?
Was soll die Funktion vectLE() sein? Hätte wohl ein ctor werden sollen, 
steht dann aber in der falschen Klasse.
Wo ist initWieCtor()?

Nochmal: mache ein MCVE, damit alle hier, die Dir helfen wollen, oder 
zumindest das Problem verstehen wollen, auch eine Chance haben.

von Oliver S. (oliverso)


Lesenswert?

Verschwindibus schrieb:
> Man merkt dir deine Verunsicherung an.

Worüber?

Verschwindibus schrieb:
> Ich habe 2 Möglichkeiten für einen cast-ctor:

Deine zwei Möglichkeiten machen das selbe noch das gleiche. Das die 
zweite gar nicht kompiliert, wurde die ja schon gesagt. Und selbst wenn 
man die korrigiert, spielt da neben den unbekannten 
Default-Konstruktoren der Basisklasse auch ein Copy-oder 
Move-Konstruktor mit, der ebenfalls unbekannt ist.

Und du fragst jetzt, warum der Compiler da unterschiedlichen Code 
generiert? (was er eh nicht tut, weil es so gar nicht kompilierbar ist).

Danach kommt dan plötzlich ein uninitialistertes Array und dubiose 
Callbacks ins Spiel.

Da kann man schon mal darüber verunsichert sein, ob das alles das nicht 
entweder völlige Ahnungslosigkeit oder reine Trollerei ist.

Oliver

von Verschwindibus (Gast)


Lesenswert?

Ich wollte eigentlich nicht noch mehr Verwirrung stiften.. :)
Es geht mir nicht darum, dass meine Syntax auf Fehler überprüft wird 
(sonst würde ich standardmäßig in clang, statt gcc programmieren ;) ).

Ich dachte eher an Ratschläge, wie: "Ach, seltsam langes ASM kommt mir 
bekannt vor, oder plötzliche Probleme mit Inlining. Versuch mal '-fXYZ', 
damit der Compiler 'W' macht."

Ich bin inzwischen selbst auf Flag-Suche gegangen und bei diversen 
"-f..inline.." fündig geworden, aber warum, im Detail, das ein oder 
andere Flag hilft, ka.

@ Wilhelm M.:
>Was soll das sein? Zudem ganz anders als Dein Eingangsproblem.

Verschwindibus schrieb:
> Man nehme, für ein entsprechendes Bsp des Problems eine Kombination der,
> jeweils mit A, B, C, D, oder E gekennzeichneten, Zeilen, entsprechend
> der unten stehenden, zugehörigen 'Rezeptur' des jew Beispiels.
> Außerdem seien in X_b, wie erwartet, natürlich auch initialisierbare
> (POD) Daten vorhanden.

Nein, mein Eingangsproblem ist unverändert: zu langes ASM.
Nur meine Fehlersuche ist vorangeschritten und der Kreis der 
Verdächtigen hat sich geändert.

>Was soll die Funktion vectLE() sein? Hätte wohl ein ctor werden sollen,
>steht dann aber in der falschen Klasse.

Stimmt, sorry, bis eben übersehen, sollte natürlich "X(..)" heißen.

>Wo ist initWieCtor()

initWieCtor(...) == X<T>( [&](bool b){ return y.g(b); } == X_b<T>( 
[&](bool b){ return y.g(b); } )
Macht was draufsteht und holt sich "y.g(b)" .

Rolf M. schrieb:
> Hmpf. Du behauptest, dass irgendein GCC da anderen Code generiert als du
> erwartest.

Hmpf. Ja.

>Der erste Schritt, um ein Problem anzugehen, ist immer der Versuch, es
>zu reproduzieren. Wenn man das nicht kann, hat man eigentlich keine
>Chance, außer es einfach hinzunehmen.

..Sag das all jenen, die nur nach kompilierbaren Beispielen verlangen. 
Hast du noch zufällig konkrete Weisheiten über Inlining-Probleme des 
gcc??


Gcc schrieb:
> Lieber TO,
> der GCC erzeugt in allen Fällen genau den selben Code:
> exakt 0 Byte.

Lieber Gcc, es wäre schön, wenn das deine Fehlermeldung gewesen wäre. 
Stattdessen hast du dich für eine zu künstlerische Interpretation meines 
Codes entschieden.

@Oliver: Du solltest Logbuchführer werden. Denn, abgesehen von deinen 
konstuierten Porblemchen, hasst du meine Reise relativ gut 
zusammengefasst.

>Da kann man schon mal darüber verunsichert sein, ob das alles das nicht
>entweder völlige Ahnungslosigkeit oder reine Trollerei ist.

Muss man aber nicht. Kann man aber, wenn man es nötig hat. Da stimme ich 
dir zu.

von Wilhelm M. (wimalopaan)


Lesenswert?

Verschwindibus schrieb:
>>Was soll die Funktion vectLE() sein? Hätte wohl ein ctor werden sollen,
>>steht dann aber in der falschen Klasse.
>
> Stimmt, sorry, bis eben übersehen, sollte natürlich "X(..)" heißen.

Das kann doch gar nicht sein, wenn das wirklich Deinem Problem 
entspricht.
Wie gesagt: poste einfach ein MCVE, dann sehen wir weiter.

Verschwindibus schrieb:
> Ich bin inzwischen selbst auf Flag-Suche gegangen und bei diversen
> "-f..inline.." fündig geworden, aber warum, im Detail, das ein oder
> andere Flag hilft, ka.

Ach, ist ja toll. Und welche möchtest Du uns nicht erzählen?

Verschwindibus schrieb:
> Ich dachte eher an Ratschläge, wie: "Ach, seltsam langes ASM kommt mir
> bekannt vor, oder plötzliche Probleme mit Inlining. Versuch mal '-fXYZ',
> damit der Compiler 'W' macht."

-fwimiwyg

von Oliver S. (oliverso)


Lesenswert?

Wilhelm M. schrieb:
> Verschwindibus schrieb:
>> Ich bin inzwischen selbst auf Flag-Suche gegangen und bei diversen
>> "-f..inline.." fündig geworden, aber warum, im Detail, das ein oder
>> andere Flag hilft, ka.
>
> Ach, ist ja toll. Und welche möchtest Du uns nicht erzählen?

Vor allem, weil die eigentlich alle bei Nutzung von -O2 oder -O3 aktiv 
sind.

Oliver

von Verschwindibus (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Das kann doch gar nicht sein, wenn das wirklich Deinem Problem
> entspricht.

So langsam denken wir in die gleiche Richtung..

> Wie gesagt: poste einfach ein MCVE, dann sehen wir weiter.

Wie gesagt, wie soll das unter diesen Umständen einfach gehen..

> Ach, ist ja toll. Und welche möchtest Du uns nicht erzählen?

Der Zyniker in mir würde jetzt sagen: Wozu, du hast doch eh kein MCVE..
Sonst vielleicht einfach normal fragen, wenns dich interessiert!?

Oliver S. schrieb:
> Vor allem, weil die eigentlich alle bei Nutzung von -O2 oder -O3 aktiv
> sind.
>
> Oliver

Ich merke schon, da kennst sich jemand aus. Ist ja nicht so, als könnte 
genau das das Problem sein.
Kannst dich ja mal fragen, warum es viele Flags mit "no-"-Präfix gibt...

von Gcc (Gast)


Lesenswert?

Da es hier wohl nur einen gibt, der wirklich was davon versteht, scheint 
das Problem gelöst.

von Wilhelm M. (wimalopaan)


Lesenswert?

Verschwindibus schrieb:
> Wilhelm M. schrieb:
>> Das kann doch gar nicht sein, wenn das wirklich Deinem Problem
>> entspricht.
>
> So langsam denken wir in die gleiche Richtung..

Glaube ich nicht!

>> Wie gesagt: poste einfach ein MCVE, dann sehen wir weiter.
>
> Wie gesagt, wie soll das unter diesen Umständen einfach gehen..

Du hast das ganze doch untersucht, oder? Also, was spricht nun dagegen, 
dieses MVCE zu posten. Denn Du musst ja eins haben, sonst könntest Du ja 
nicht so sicher sein.

>> Ach, ist ja toll. Und welche möchtest Du uns nicht erzählen?
>
> Der Zyniker in mir würde jetzt sagen: Wozu, du hast doch eh kein MCVE..
> Sonst vielleicht einfach normal fragen, wenns dich interessiert!?

Ich habe jetzt schon so oft nach einem MVCE gefragt, doch Du trollst nur 
rum!

von Rolf M. (rmagnus)


Lesenswert?

Verschwindibus schrieb:
>>Der erste Schritt, um ein Problem anzugehen, ist immer der Versuch, es
>>zu reproduzieren. Wenn man das nicht kann, hat man eigentlich keine
>>Chance, außer es einfach hinzunehmen.
>
> ..Sag das all jenen, die nur nach kompilierbaren Beispielen verlangen.

Ich sage es dir, weil gerade das der Grund ist, warum sie ein solches 
Beispiel verlangen. Wenn keiner dein Problem reproduzieren kann, kann 
auch keiner was dazu sagen.

von DerEgon (Gast)


Lesenswert?

Rolf M. schrieb:
> Wenn keiner dein Problem reproduzieren kann, kann
> auch keiner was dazu sagen.

Das ist vermutlich wie bei Heisenberg: Entweder man kann das Problem 
reproduzieren oder etwas zu dessen Lösung beitragen.

von Verschwindibus (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Glaube ich nicht!

Schade, ich bin eben ein Optimist. Und du?

> Du hast das ganze doch untersucht, oder? Also, was spricht nun dagegen,
> dieses MVCE zu posten. Denn Du musst ja eins haben, sonst könntest Du ja
> nicht so sicher sein.
> Ich habe jetzt schon so oft nach einem MVCE gefragt, doch Du trollst nur
> rum!

Du scheinst anzunehmen, dass ich mir eines Tages gedacht habe: So ich 
mach mir jetzt nen MCVE zu einem unvorhergesehenen Problem..

Verschwindibus schrieb:
> Mit anderen Worten, jemand, der mir helfen kann, sollte jetzt schon mehr
> parat haben, als dumme Sprüche und Geweine.

Rolf M. schrieb:
> Ich sage es dir, weil gerade das der Grund ist, warum sie ein solches
> Beispiel verlangen. Wenn keiner dein Problem reproduzieren kann, kann
> auch keiner was dazu sagen.

Dazu du selbst:

Rolf M. schrieb:
> Wenn man das nicht kann, hat man eigentlich keine
> Chance, außer es einfach hinzunehmen.

Und ich bin durchaus der Meinung, dass man auch dann etwas dazu sagen 
kann, wenn man kann.
Um das zu verdeutlichen:
Mal angenommen, es gäbe ein MCVE und das Problem wäre reproduzierbar: 
Hätten die Leute mehr parat als dann selbst nur rumzuprobieren, nämlich 
Tipps, könnte man sie auch jetzt schon geben.

DerEgon schrieb:
> Das ist vermutlich wie bei Heisenberg: Entweder man kann das Problem
> reproduzieren oder etwas zu dessen Lösung beitragen.

..wegen seiner Unschärferelation, oder doch seiner ..Katze..??

Gcc schrieb:
> Da es hier wohl nur einen gibt, der wirklich was davon versteht, scheint
> das Problem gelöst.

Wenigstens das ist eine gelungene Darbietung: Genau was der gcc sagen 
würde! :)

von DerEgon (Gast)


Lesenswert?

Verschwindibus schrieb:
> ..wegen seiner Unschärferelation, oder doch seiner ..Katze..??

Heisenbergs Katze oder Schrödingers Unschärferelation, das ist hier die 
Frage (einen Totenschädel anblickend).

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Verschwindibus schrieb:
> Und ich bin durchaus der Meinung, dass man auch dann etwas dazu sagen
> kann, wenn man kann.

Also ich hatte schon mit vielen Problemen dieser Art zu tun, aber da 
jetzt allgemein ohne konkretes Beispiel was zu sagen ist schwierig. Da 
kann man nur so Allgemeinplätze abgeben: Optimierungen einschalten, alle 
move/copy Konstruktoren und assignment Operatoren korrekt 
implementieren, viel constexpr und inline verwenden, kein volatile o.ä. 
verwenden, kein reinterpret_cast oder sonstige Speicher-Schweinereien 
verwenden, immer direkte Initialisierung statt nachträglicher Zuweisung, 
alles const-Korrekt und Move-Korrekt implementieren, bei Integer-Typen 
auf Promotion achten, std::forward verwenden... Es gibt bestimmt noch 
mehr, aber ohne das Problem am konkreten Code zu sehen fällt mir gerade 
nicht mehr ein.

Solche Probleme habe ich immer gelöst indem ich mir den fraglichen 
Assembler-Code angeschaut habe, den Source-Code immer weiter reduziert 
und geprüft dass das Problem bestehen bleibt. Irgendwann stößt man auf 
die Stelle, die das Problem verursacht.

Ich bin jetzt zwar auch neugierig was genau die Ursache ist aber wenn du 
kein vollständiges Beispiel rausrückst, ist es halt so.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Verschwindibus schrieb:
> Wilhelm M. schrieb:
>> Glaube ich nicht!
>
> Schade, ich bin eben ein Optimist. Und du?
>
>> Du hast das ganze doch untersucht, oder? Also, was spricht nun dagegen,
>> dieses MVCE zu posten. Denn Du musst ja eins haben, sonst könntest Du ja
>> nicht so sicher sein.
>> Ich habe jetzt schon so oft nach einem MVCE gefragt, doch Du trollst nur
>> rum!
>
> Du scheinst anzunehmen, dass ich mir eines Tages gedacht habe: So ich
> mach mir jetzt nen MCVE zu einem unvorhergesehenen Problem..

Nein.

Fakt ist ein: Du hast ein reales Problem.

Und offensichtlich hast Du es schon so weit herunter gebrochen, dass Du 
Dir den Assembler-Code dazu angesehen hast. Du bist also jetzt schon den 
halben Weg bis zu einem MVCE gegangen.
Und Du hast versucht, aus diesem tatsächlichen Code ein MVCE zu 
extrahieren (was Dir allerdings nicht ganz gelungen ist, denn die 
Code-Schnipsel, die Du postest waren bisher fehlerhaft und 
unvollständig). Wer weiß, welche anderen Unterschiede noch zu Deinem 
wirklichen Code vorhanden sind?

Solange also nichts konkretes kommt von Dir, bist Du wohl auf Dich 
allein gestellt.

von Oliver S. (oliverso)


Lesenswert?

Verschwindibus schrieb:
> Mal angenommen, es gäbe ein MCVE und das Problem wäre reproduzierbar:
> Hätten die Leute mehr parat als dann selbst nur rumzuprobieren, nämlich
> Tipps, könnte man sie auch jetzt schon geben.

Nein.
Denn wenn dein Code dem des Ausgangsbeitrags ähnelt, dann vergleichst du 
Äpfel mit faulen Birnen, und wunderst dich, daß in beiden Fällen kein 
Pflaumenmus rauskommt. Sehr vermutlich ist dein Sourcecode fragwürdig, 
nicht der Compiler.

Einen Tipp kann man allerdings tatsächlich geben:

Die Frage, warum ein Compiler etwas macht, wie er es macht, ist nie 
zielführend.

Oliver

von Verschwindibus (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Fakt ist ein: Du hast ein reales Problem.

Ach, plötzlich bin ich doch kein Troll.

> Und offensichtlich hast Du es schon so weit herunter gebrochen, dass Du
> Dir den Assembler-Code dazu angesehen hast. Du bist also jetzt schon den
> halben Weg bis zu einem MVCE gegangen.
> Und Du hast versucht, aus diesem tatsächlichen Code ein MVCE zu
> extrahieren (was Dir allerdings nicht ganz gelungen ist, denn die
> Code-Schnipsel, die Du postest waren bisher fehlerhaft und
> unvollständig).

MCVE.
Beruht dein programmierstil auch auf solchen "Offensichtlichkeiten"? Du 
liegst nämlich falsch. ASM guckt man sich z.B. auch an, wenn man 
verschiedene Implementationen miteinander vergleicht. Ebenso falsch sind 
deine Beobachtungen: Ich wollte nie ein MCVE extrahieren.

> Wer weiß, welche anderen Unterschiede noch zu Deinem
> wirklichen Code vorhanden sind?

Ich.

> Solange also nichts konkretes kommt von Dir, bist Du wohl auf Dich
> allein gestellt.

Das zu behaupten, direkt nachdem mir jemand Tipps gegeben hat, zeugt von 
einer ganz speziellen Art einer offenen Herangehensweise...
Ganz schöner Umweg um mir mitzuteilen, dass du eigentlich gar keine 
Ahnung hast, unter diesen Umständen.

Oliver S. schrieb:
> Nein.

Auch an dich: Das zu behaupten, direkt nachdem mir jemand Tipps gegeben 
hat, zeugt von einer ganz speziellen Art einer offenen 
Herangehensweise...

> Denn wenn dein Code dem des Ausgangsbeitrags ähnelt, dann vergleichst du
> Äpfel mit faulen Birnen, und wunderst dich, daß in beiden Fällen kein
> Pflaumenmus rauskommt. Sehr vermutlich ist dein Sourcecode fragwürdig,
> nicht der Compiler.

> Ganz schöner Umweg um mir mitzuteilen, dass du eigentlich gar keine Ahnung
> hast, unter diesen Umständen.

..Äpfen, faule Birnen, Pflaumen, ...Klingt für mich nach C++-Alltag..

> Einen Tipp kann man allerdings tatsächlich geben:
>
> Die Frage, warum ein Compiler etwas macht, wie er es macht, ist nie
> zielführend.

Was für ein Nicht-Tipp! Das lässt mich vermuten, dass du dich kein Stück 
mit Compilern auskennst. Du hast die Zeit anscheinend nicht genutzt, um 
mal herauszufinden, warum es viele Flags mit 'no-' gibt.

Niklas G. schrieb:
> Also ich hatte schon mit vielen Problemen dieser Art zu tun, aber da
> jetzt allgemein ohne konkretes Beispiel was zu sagen ist schwierig. Da
> kann man nur so Allgemeinplätze abgeben:
> Optimierungen einschalten

Check: wobei der ASM mit < O3 noch knackiger wird... ._. (als würde er 
sich selbst ein Bein stellen. Aber warum. Könnte der Code, meinetwegen, 
zu kein sein? )

> alle move/copy Konstruktoren und assignment Operatoren korrekt
> implementieren

Check: entweder nie benutzt, implizit, oder manuell erstellt.

> viel constexpr und inline verwenden, kein volatile o.ä. verwenden

Aber sowas von Check.

> kein reinterpret_cast oder sonstige Speicher-Schweinereien verwenden

Ein zivilisiertes Check. (Wobei ich nicht weiß, ob mein Callback-Aufruf 
nicht irgendwie dazu zählt...)

> immer direkte Initialisierung statt nachträglicher Zuweisung,

Ca.-Ckeck: Gerade das Gegenteil führt manchmal zum Erfolg.

> alles const-Korrekt und Move-Korrekt implementieren, bei Integer-Typen
> auf Promotion achten, std::forward verwenden...

Check: Macht keinen Unterschied, ob alles mit const ref, oder univ. ref. 
Und gibt nur einen int-Typ.

> Es gibt bestimmt noch
> mehr, aber ohne das Problem am konkreten Code zu sehen fällt mir gerade
> nicht mehr ein.
>
> Solche Probleme habe ich immer gelöst indem ich mir den fraglichen
> Assembler-Code angeschaut habe, den Source-Code immer weiter reduziert
> und geprüft dass das Problem bestehen bleibt. Irgendwann stößt man auf
> die Stelle, die das Problem verursacht.
>
> Ich bin jetzt zwar auch neugierig was genau die Ursache ist aber wenn du
> kein vollständiges Beispiel rausrückst, ist es halt so.

Ich danke dir so oder so, schon mal für deine Hilfe. Das lässt mich 
wenigstens hoffen, dass ich nicht der Verrückte bin.. :)
Das Problem ist leider Teil eines Projektes und wurde noch nicht 
extrahiert.
Daher kann ich nur weitere Beobachtungen zum drüber Nachdenken teilen:

Mal zurück zu dem obigen Beispiel der Klasse X. Die 'Rezepte' besitzen 
noch immer ihre Gültigkeit:

Ich habe es mit "inline __attribute((always_inline))__ X().." probiert:
-> Gar kein Inlining mehr!

Ich habe die Logik (quasi Rezept 'E') in eine Wrapper-Funktion 
(ent-)gepackt:
-> Die ANZAHL der Argumente (auch wenn defaulted) macht einen 
Unterschied! <--- ???

Habe es mit verzögerter, verzögerter Init ( Cpy cpy; cpy.init(..); *this 
= cpy; ) versucht (ähnlich wie in "Fall 2" eingangs):
-> X_b besitze einen Ctor X_b(){ /*leer*/ } ! Dennoch macht es einen 
Unterschied, ob ich "Cpy cpy;" , oder "Cpy cpy{};" nehme.

Nun.. Und immer stellt sich mir die Frage: Soll das so sein..? (Btw. 
Clang sagt Nein.)

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Verschwindibus schrieb:
> Check: wobei der ASM mit < O3 noch knackiger wird...

-O2 reicht eigentlich um "Dummheiten" und Umwege loszuwerden.

Verschwindibus schrieb:
> Check: entweder nie benutzt, implizit, oder manuell erstellt.

Ich tendiere dazu die alle immer manuell zu erstellen oder mit 
"=delete"/"=default" zu definieren, damit ganz sicher ist, dass sie 
existieren oder nicht existieren (je nachdem was benötigt).

Verschwindibus schrieb:
> (Wobei ich nicht weiß, ob mein Callback-Aufruf
> nicht irgendwie dazu zählt...)

Wenn das ein Aufruf über Funktionszeiger oder virtuelle Funktion ist, 
kann der Compiler den wahrscheinlich nicht optimieren, es sei denn das 
ganze Konstrukt ist "inline".

Verschwindibus schrieb:
> Ca.-Ckeck: Gerade das Gegenteil führt manchmal zum Erfolg.

Das entspricht nicht meinen Erfahrungen und deutet IMO auf einen Fehler 
im Code hin.

Verschwindibus schrieb:
> Check: Macht keinen Unterschied, ob alles mit const ref, oder univ. ref.

Kommt auf den Code an. i.A. sollten die Referenztypen schon überall 
korrekt sein.

Verschwindibus schrieb:
> Ich habe es mit "inline __attribute((always_inline))__ X().." probiert:

Muss es nicht "__attribute__((always_inline)) inline" sein?

> -> Gar kein Inlining mehr!

Das kann nicht. Der GCC befolgt always_inline immer.

Verschwindibus schrieb:
> -> Die ANZAHL der Argumente (auch wenn defaulted) macht einen
> Unterschied! <--- ???

Kann schon sein, der Inlining-Algorithmus arbeitet heuristisch (außer 
always_inline ist vorhanden).

Verschwindibus schrieb:
> Nun.. Und immer stellt sich mir die Frage: Soll das so sein..?

Nein, der GCC kriegt sowas normalerweise gut hin. Verwendest du denn 
eine aktuelle Version?

: Bearbeitet durch User
von Verschwindibus (Gast)


Lesenswert?

Niklas G. schrieb:
> -O2 reicht eigentlich um "Dummheiten" und Umwege loszuwerden.

So wie es sich mir darstellt, tritt das Problem erst ab -O2 auf, und 
auch der sonstige Code ist mit -O0 knackiger. Muss ich aber nochmal 
durchprobieren..
Aber unter welchen Bedingungen, könnte das überhaupt der Fall sein?

Niklas G. schrieb:
> ie alle immer manuell zu erstellen oder mit
> "=delete"/"=default" zu definieren, damit ganz sicher ist, dass sie
> existieren oder nicht existieren (je nachdem was benötigt).

Alles durchiteriert. Hat keinen Einfluss.

Niklas G. schrieb:
> Wenn das ein Aufruf über Funktionszeiger oder virtuelle Funktion ist,
> kann der Compiler den wahrscheinlich nicht optimieren, es sei denn das
> ganze Konstrukt ist "inline".

Ist zur Compile-Zeit bekannt (Template-Par.).

Niklas G. schrieb:
> Das entspricht nicht meinen Erfahrungen und deutet IMO auf einen Fehler
> im Code hin.

Ne, meinen auch nicht.
Code, der das Problem auftretem/verschwinden lässt, ist allerdings an 
mehreren, (augenscheinlich) von einander unabhängigen, Ecken zu finden.

Niklas G. schrieb:
> Kommt auf den Code an. i.A. sollten die Referenztypen schon überall
> korrekt sein.

Ich meinte, dass ich es mit 2 Versionen versucht habe. Die "einfache", 
bei der man nicht mal eben ein std::forward vergessen kann und die 
"moderne".

Niklas G. schrieb:
> Muss es nicht "__attribute__((always_inline)) inline" sein?

Ich glaube nicht. So gerade ausprobert: Macht keinen Unterschied.
Und noch ein Spaß für zwischendurch: Wenn ich im Beispiel 
"Wrapper-Funktion" sowohl Ctor, als auch die Wrapper-Funktion damit 
versehe, wird der Callback jedenfalls gar nicht ge-inlined..

Niklas G. schrieb:
> Das kann nicht. Der GCC befolgt always_inline immer.

Finde ich auch! Kann auch nichts weiter dazu sagen.

Wobei, ich könnte mir vorstellen, dass der Compiler sich in dieser 
Situation evtl zwischen 2 Inline-Möglichkeiten entscheiden muss, wovon 
eine dann zu weiteren Optimierungen führt.
Dass das ASM mit -O0 noch kleiner ist, gefällt mir dabei persönlich 
weniger..

Niklas G. schrieb:
> Kann schon sein, der Inlining-Algorithmus arbeitet heuristisch (außer
> always_inline ist vorhanden).

Ich hätte noch erwähnen sollen, dass die zusätzlichen Argumente nicht 
genutzt werden, falls das einen Unterschied macht. Und alles schön 
sichtbar sein sollte für den Compiler.

Niklas G. schrieb:
> Nein, der GCC kriegt sowas normalerweise gut hin. Verwendest du denn
> eine aktuelle Version?

Jup, gerade frisch eine 12.x..

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Verschwindibus schrieb:
> So wie es sich mir darstellt, tritt das Problem erst ab -O2 auf, und
> auch der sonstige Code ist mit -O0 knackiger.

Seltsam. Normalerweise macht der GCC bei -O0 ziemliche Ausschweife, bei 
-O2 ist eh kompakt und schnell, bei -O3 wird er gern mal wieder länger 
aber dafür noch schneller.

Verschwindibus schrieb:
> Wenn ich im Beispiel
> "Wrapper-Funktion" sowohl Ctor, als auch die Wrapper-Funktion damit
> versehe, wird der Callback jedenfalls gar nicht ge-inlined..

Der Callback selber muss natürlich auch "always_inline" sein, nicht nur 
der Aufrufer!

Verschwindibus schrieb:
> Niklas G. schrieb:
>> Das kann nicht. Der GCC befolgt always_inline immer.
>
> Finde ich auch! Kann auch nichts weiter dazu sagen.

Außer du nimmst irgendwo einen Zeiger auf die Funktion, oder sie ist 
virtuell, dann muss sie natürlich "explizit" existieren.

Verschwindibus schrieb:
> Dass das ASM mit -O0 noch kleiner ist, gefällt mir dabei persönlich
> weniger..

Gerade bei x86 kann kleiner=langsamer sein.

von Verschwindibus (Gast)


Lesenswert?

Sorry, ich hab jetzt nochmal nachgeschaut: Ich meinte nicht "-O0", 
sondern "-O". Mit "-O1" gibt's gleiche Ergebnisse:
1
  .cfi_startproc
2
  subq  $8, %rsp
3
  .cfi_def_cfa_offset 16
4
  leaq  .LC0(%rip), %rsi
5
  leaq  _ZSt4cout(%rip), %rdi
6
  call  _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
7
  movq  %rax, %rdi
8
  movsd  .LC1(%rip), %xmm0
9
  call  _ZNSo9_M_insertIdEERSoT_@PLT
10
  movq  %rax, %rdi
11
  call  _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@PLT
12
  movl  $0, %eax
13
  addq  $8, %rsp
14
  .cfi_def_cfa_offset 8
15
  ret
16
  .cfi_endproc

Was, ziemlich exakt sein sollte, da ich nur eine Berechnung mit Literals 
ausführe und ausgebe. Und alles unnötige scheint auch verworfen zu 
werden.

ASM mit "-O3":
1
  .cfi_startproc
2
  pushq  %rbp
3
  .cfi_def_cfa_offset 16
4
  .cfi_offset 6, -16
5
  movl  $13, %edx
6
  leaq  .LC0(%rip), %rsi
7
  pushq  %rbx
8
  .cfi_def_cfa_offset 24
9
  .cfi_offset 3, -24
10
  leaq  _ZSt4cout(%rip), %rbx
11
  movq  %rbx, %rdi
12
  subq  $8, %rsp
13
  .cfi_def_cfa_offset 32
14
  call  _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l@PLT
15
  movsd  .LC1(%rip), %xmm0
16
  movq  %rbx, %rdi
17
  call  _ZNSo9_M_insertIdEERSoT_@PLT
18
  movq  %rax, %rbx
19
  movq  (%rax), %rax
20
  movq  -24(%rax), %rax
21
  movq  240(%rbx,%rax), %rbp
22
  testq  %rbp, %rbp
23
  je  .L7
24
  cmpb  $0, 56(%rbp)
25
  je  .L3
26
  movsbl  67(%rbp), %esi
27
.L4:
28
  movq  %rbx, %rdi
29
  call  _ZNSo3putEc@PLT
30
  movq  %rax, %rdi
31
  call  _ZNSo5flushEv@PLT
32
  addq  $8, %rsp
33
  .cfi_remember_state
34
  .cfi_def_cfa_offset 24
35
  xorl  %eax, %eax
36
  popq  %rbx
37
  .cfi_def_cfa_offset 16
38
  popq  %rbp
39
  .cfi_def_cfa_offset 8
40
  ret
41
.L3:
42
  .cfi_restore_state
43
  movq  %rbp, %rdi
44
  call  _ZNKSt5ctypeIcE13_M_widen_initEv@PLT
45
  movq  0(%rbp), %rax
46
  movl  $10, %esi
47
  movq  %rbp, %rdi
48
  call  *48(%rax)
49
  movsbl  %al, %esi
50
  jmp  .L4
51
.L7:
52
  call  _ZSt16__throw_bad_castv@PLT
53
  .cfi_endproc

Mit -O3 wird der Code 'ohne das Problem' zwar länger, aber bleibt 
konstant. Damit kann ich leben. Warum das passiert, würde mich trotzdem 
interessieren...
Warum plötzlich z.B. __throw_bad_castv und die ganzen jumps..?

Nur 'mit dem Problem' wird, dank evtl. unpassendem Inlining (?), der 
Code beliebig lang, je nach Inhalt des Callbacks.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Disassembliere doch bitte mal mit "-C" damit es lesbarer wird...

von Oliver S. (oliverso)


Lesenswert?

Verschwindibus schrieb:
> call  _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT

vs.

Verschwindibus schrieb:
> call
> _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_P 
KS3_l@PLT

Der Compiler scheint anzunehmen, daß letzteres schneller ist, trotz des 
Mehraufwandes drum herum.

Oliver

von Verschwindibus (Gast)


Lesenswert?

Also ich habe noch weiter getestet und dabei festgestellt, dass C++20 
noch nicht einheitlich behandelt wird von unterschiedlichen Compilern. 
Und wenn das schon der Fall ist, ist dieser Heuristik-Schluckauf etwas, 
womit man sich abfinden kann..
Danke dennoch.

@Niklas G.: Womit bekommt man denn lesbareren Code raus und was bedeutet 
lesbar für dich? Das war jetzt das Zwischenergebnis direkt vom Compiler.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Verschwindibus schrieb:
> Das war jetzt das Zwischenergebnis direkt vom Compiler.

Ich bevorzuge es, die finale Anwendung zu disassemblieren, weil die .o 
-Dateien ja auch noch einmal verarbeitet werden. Und dann wie gesagt mit 
"objdump -d -C application > disassembly.S" o.ä.

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.