Darf Compiler member entfernen?



  • Hallo,
    angenommen ich habe ein struct, das Member-Variablen enthält, die nirgendwo im Programm verwendet werden. Darf der Compiler (bzw. der Optimierer) diese Variablen entfernen, um Speicher zu sparen (und damit auch das Speicher-Layout des structs verändern)?

    Oder ist durch den Standard garantiert, dass das nicht gemacht wird?

    Und gilt hier das selbe für C wie für C++ (habe in meinem Programm eine Schnittstelle von C++ zu C)?



  • Ja, so lange sich dadurch nicht das beobachtbare Verhalten des Programms ändert. D.h. sobald du z.B. einen Zeiger auf eine Instanz eines structs an irgendeine Funktion übergibst, die nicht im aktuell compilierten Source* enthalten ist, muss der Compiler natürlich davon ausgehen dass in dem Moment die ganze Instanz "beobachtbar" geworden ist. D.h. die muss dann vollständig und im erwarteten Layout vorliegen.

    Praktisch werden solche Optimierungen vermutlich nur selektiv und nur lokal passieren. Also z.B. wenn der Compiler eine Funktion inlined und sieht dass von der übergebenen struct bloss ein Member verwendet wird, dann wird er normalerweise nicht die anderen Member in den Speicher schreiben. Und das eine Member wird vermutlich in einem Register landen.

    habe in meinem Programm eine Schnittstelle von C++ zu C

    Das funktioniert normalerweise. Natürlich müssen der C++ und der C Compiler Dinge wie Alignment und Structure-Packing gleich machen. Aber das sollte passen wenn beide mit den für die Plattform üblichen Defaults arbeiten.
    Die meisten Betriebssystem Schnittstellen arbeiten auch so, d.h. wenn das nicht funktionieren würde, dann könnte man keine WinAPI oder POSIX Funktionen in einem C++ Programm verwenden die irgendwo structs oder Zeiger auf structs als Parameter haben.

    *: Damit meine ich alles was der Compiler/Linker (bzw. "die Implementierung") irgendwie zu sehen bekommt. D.h. wenn man Dingen wie "Whole Program Optimization" verwendet, dann ist das das gesamte Programm das man baut. Aber halt nicht irgendwelche OS-Funktionen, und auch nicht Teile eines anderen Projekts (z.B. DLL/SO) die der Compiler/Linker nie gesehen hat und nie sehen wird.



  • @happystudent
    Ich glaube, das darf er nicht. Der Compiler darf noch nicht einmal die struct members umsortieren, um eine optimalere Speicherausnutzung (weniger Padding-Bytes) zu erreichen. Kann aber sein dass ich falsch liege. Warte noch auf andere Antworten ...



  • Why not? Solange as-if:

    [intro.abstract]/1:

    The semantic descriptions in this document define a parameterized nondeterministic abstract machine. This document places no requirement on the structure of conforming implementations. In particular, they need not copy or emulate the structure of the abstract machine. Rather, conforming implementations are required to emulate (only) the observable behavior of the abstract machine* as explained below.4

    [...]

    4) This provision is sometimes called the “as-if” rule, because an implementation is free to disregard any requirement of this document as long as the result is as if the requirement had been obeyed, as far as can be determined from the observable behavior of the program. For instance, an actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no side effects affecting the observable behavior of the program are produced.

    Scalar Replacement of Aggregates ist auch sehr beliebt.



  • Ich stelle mir schwierig vor, überhaupt rauszufinden, ob der Compiler "dead store" in structs wegoptimiert hat.
    Dass das bei lokalen Variablen passieren kann, hat ja schon Hustbär geschrieben.
    sizeof() wird mit Sicherheit immer die volle Länge liefern, auch wenn nur auf ein einziges Element zugegriffen wird.



  • @swordfish sagte in Darf Compiler member entfernen?:

    Why not? Solange as-if:

    Naja, aus eben dem Grund den auch schon @Regenbogenschaf genannt hat.Die Anordnung der Member darf ja auch nicht verändert werden, obwohl das für das theoretische Verhalten ja überhaupt keinen Unterschied macht.

    Es scheint also noch mehr Ausnahmen in diesem Zusammenhang zu geben, als unter dem von dir geposteten Link aufgelistet sind.

    @hustbaer Ist das also nur bei calls von "für den Compiler nicht sichtbaren" Code garantiert?



  • @happystudent sagte in Darf Compiler member entfernen?:

    Naja, aus eben dem Grund den auch schon @Regenbogenschaf genannt hat. Die Anordnung der Member darf ja auch nicht verändert werden,

    Um das beobachten zu können müsste ein Programm erst einmal die Adressen von zwei Membern nehmen. Dazu müssen die Dinger dann auf jeden Fall existieren.



  • @swordfish sagte in Darf Compiler member entfernen?:

    Um das beobachten zu können müsste ein Programm erst einmal die Adressen von zwei Membern nehmen. Dazu müssen die Dinger dann auf jeden Fall existieren.

    Sie werden schon deshalb existieren, weil das Programm ihre Adresse nimmt. ☺



  • @regenbogenschaf Nichts anderes habe ich geschrieben 🙄



  • @swordfish sagte in Darf Compiler member entfernen?:

    Nichts anderes habe ich geschrieben

    Du schriebst "dazu müssen" und nicht "deshalb werden". Aus deinen Worten wird nicht ersichtlich, was Ursache und was Wirkung ist.



  • Yes, no, is clear.



  • @swordfish : Deinen Text kann man auch so verstehen, dass das Ermitteln der Adresse fehlschlägt, sollte der Member wegoptimiert worden sein.



  • Yes, no, is clear.



  • Warum muß dazu erst die Adresse eines Members benutzt werden? Man könnte auch einfach den gesamten Speicher auslesen (wenn man vorher bestimmte Werte den einzelnen Membern zugewiesen hat)..



  • @swordfish sagte in Darf Compiler member entfernen?:

    Um das beobachten zu können müsste ein Programm erst einmal die Adressen von zwei Membern nehmen. Dazu müssen die Dinger dann auf jeden Fall existieren.

    Ja, aber das kriegt der Compiler von Programm A nicht mit, wenn Programm B (auf welche Art auch immer) den Speicher von A ausliest.



  • @th69 könnte klappen:

    struct test {char c1; char c2; char c3;};
    struct test t;
    t.c1 = 1;
    t.c3 = 2;
    char *p = (char*)&t;
    printf ("%d, %d, %d\n", p[0], p[1], p[2]);
    
    

    Kommt 1,irgendwas,2, wurde c2 nicht wegoptimiert.
    kommt 1,2,irgendwas, ist c2 nicht da.



  • @happystudent sagte in Darf Compiler member entfernen?:

    Ja, aber das kriegt der Compiler von Programm A nicht mit, wenn Programm B (auf welche Art auch immer) den Speicher von A ausliest.

    Das hat nichts mehr mit dem Standard zu tun.



  • @th69 sagte in Darf Compiler member entfernen?:

    Warum muß dazu erst die Adresse eines Members benutzt werden? Man könnte auch einfach den gesamten Speicher auslesen (wenn man vorher bestimmte Werte den einzelnen Membern zugewiesen hat).

    Dazu müssen die Member sowieso existieren also im Kontext der Fragestellung in diesem Thread ein no brainer.



  • @regenbogenschaf sagte in Darf Compiler member entfernen?:

    @th69 könnte klappen:

    struct test {char c1; char c2; char c3;};
    struct test t;
    t.c1 = 1;
    t.c3 = 2;
    char *p = (char*)&t;
    printf ("%d, %d, %d\n", p[0], p[1], p[2]);
    
    

    Kommt 1,irgendwas,2, wurde c2 nicht wegoptimiert.
    kommt 1,2,irgendwas, ist c2 nicht da.

    Kommt ... undefined behaviour. Ne, sorry, nicht ub. Aber es können trotzdem padding-bytes sein.



  • @swordfish sagte in Darf Compiler member entfernen?:

    Kommt ... undefined behaviour.

    Ja, und allein schon die Adresse von t zu nehmen, dürfte den Compiler dazu bringen, den unbenutzten Member nicht wegzuoptimieren.