antisteo hat geschrieben:Wieso ist ein Iterator-Makro (theor. Klasse auf dem Stack etc.) so viel schneller als eine For-Schleife, auf die ja die Compiler noch eher getrimmt sein müssten?
Als Iterator benutze ich einen nackten Zeiger. Aus
for(int j = 0; j < 100; ++j) {
foo(array[j]);
}
wird
for(auto it = array; it < array + 100; ++it) {
foo(*it);
}
was zwei statt drei Register verbraucht; daher die Platzersparnis.
antisteo hat geschrieben:Letzendlich müssten doch beide Varianten in den gleichen Zwischencode münden (was es aber bei zahlreichen deiner Beispiele nicht tut) und am Ende auch der Maschinencode ähnlich klein werden. Oder hat der MS Compiler keinen einheitlichen Zwischencode? (bzw. lässt nicht immer alle Passes über den gesamten Code laufen...)
Das Problem an Visual Studio ist die Abwärtskompatibilität: Weil er noch aus einer Zeit stammt, in der Compiler nicht viel mehr als direkte Übersetzung gemacht haben, sind Transformationen wie
if-else-zu-
switch (und umgekehrt) nicht vorhanden, damit der Programmierer entscheiden kann, was in seinem Fall schneller ist. Ich vermute, dass der Programmierer im Fall der Schleifen die Wahl haben soll zwischen Decoding-Last (Zähler) und ALU-Last (Iterator). Das war IIRC auch deren Argument gegen Single-Call Site Inlining – da du selbst mit Profiling kaum Möglichkeit hast, dem Compiler Dinge mitzuteilen wie
hier brauche ich jetzt mehr ALU-Leistung oder
hier bitte keine Lokalität, ist ihnen ein unselbstständiger Compiler mit kalkulierbarer Maschinentexterzeugung wohl lieber als ein selbstständiger, der sich unaufhaltsam falsch entscheidet. Einfach alles zu transformieren würde wohl viel alten Code langsamer machen; und stänkern würden genau die Leute, die ihn schon von Hand optimiert haben, weil wer anders nie misst.
Du kannst ja mal ausprobieren, ob Clang die Schleifen derart transformiert.