Warum füllt gcc das gesamte Array mit Nullen anstatt nur mit den verbleibenden 96 Ganzzahlen? Die Nicht-Null-Initialisierer befinden sich alle am Anfang des Arrays.
void *sink;
void bar() {
int a[100]{1,2,3,4};
sink = a; // a escapes the function
asm("":::"memory"); // and compiler memory barrier
// forces the compiler to materialize a[] in memory instead of optimizing away
}
MinGW8.1 und gcc9.2 machen beide asm ( Godbolt Compiler Explorer ).
# gcc9.2 -O3 -m32 -mno-sse
bar():
push edi # save call-preserved EDI which rep stos uses
xor eax, eax # eax=0
mov ecx, 100 # repeat-count = 100
sub esp, 400 # reserve 400 bytes on the stack
mov edi, esp # dst for rep stos
mov DWORD PTR sink, esp # sink = a
rep stosd # memset(a, 0, 400)
mov DWORD PTR [esp], 1 # then store the non-zero initializers
mov DWORD PTR [esp+4], 2 # over the zeroed part of the array
mov DWORD PTR [esp+8], 3
mov DWORD PTR [esp+12], 4
# memory barrier empty asm statement is here.
add esp, 400 # cleanup the stack
pop edi # and restore caller's EDI
ret
(Wenn SSE aktiviert ist, werden alle 4 Initialisierer mit movdqa load / store kopiert.)
Warum macht GCC nicht nur die letzten 96 Elemente lea edi, [esp+16]
und memset (mit rep stosd
), wie es Clang tut? Ist dies eine verpasste Optimierung oder ist es irgendwie effizienter, dies auf diese Weise zu tun? (Clang ruft tatsächlich an, memset
anstatt zu inlinieren rep stos
)
Anmerkung des Herausgebers: Die Frage hatte ursprünglich eine nicht optimierte Compilerausgabe, die auf die gleiche Weise funktionierte, aber ineffizienter Code -O0
beweist nichts. Es stellt sich jedoch heraus, dass diese Optimierung von GCC auch bei verfehlt wird -O3
.
Das Übergeben eines Zeigers an a
eine Nicht-Inline-Funktion wäre eine weitere Möglichkeit, den Compiler zum Materialisieren zu zwingen a[]
, jedoch in 32-Bit-Code, was zu einer erheblichen Unordnung des ASM führt. (Stapelargumente führen zu Pushs, die mit Speichern im Stapel gemischt werden, um das Array zu initiieren.)
Mit using volatile a[100]{1,2,3,4}
wird GCC erstellt, um das Array zu erstellen und dann zu kopieren , was verrückt ist. Normalerweise volatile
ist es gut zu sehen, wie Compiler lokale Variablen initiieren oder auf dem Stapel auslegen.
.rodata
... zurück. Ich kann nicht glauben, dass das Kopieren von 400 Bytes schneller ist als das Nullstellen und Festlegen von 8 Elementen.
-O3
(was auch passiert). godbolt.org/z/rh_TNF
missed-optimization
Schlüsselwort.
a[0] = 0;
und danna[0] = 1;
.