Ich hatte lange Zeit den gcc 2.7.x benutzt.
Zu der Zeit gab es den Original gcc und denjenigen von ECGS.
Der gcc 2.8.x produzierte Executables, die eine winzige Spur langsamer waren als zuvor.
Die ECGS-Variante produzierte Exe, die 10-20% langsamer waren.
Dann gab es das Verschwinden des Original gcc. Hernach existierte nur die ECGS-Linie.
Der gcc 2.95.x ist also Bestandteil des langsameren ECGS-Zweiges.
Jedoch der gcc 3.3.x produziert Kode (ShellBsh/KommandoReadv test-Skript), der um
mehr als 10% langsamer ist als bei gcc 2.95.x.
Der gcc 4.0.x produziert hier noch etwas langsameren Kode als 3.3.x.
Hinzu kommt, daß die Größe der Exe proportional mit dem Langsamerwerden steigt.
Einzig der icc 8.1 ist mit dem gcc 2.95.x gleichauf, produziert jedoch um 50% (!) größeren Kode.
Wo soll das hinführen?
Ich habe oft festgestellt, daß elementare Lösungen ohne Fisimatenten die effizientesten sind! ...
Ich stellte fest, daß früher gcc -O1 abc.c bereits zu kleinem und schnellen Kode führte. Das kann man heute mit gcc3, gcc4 und icc nicht mehr machen, sondern es muß eine Fülle von Optionen angegeben werden, um überhaupt in die Nähe der früheren Kompilierleistungen hinsichtlich Kodegeschwindigkeit zu kommen.
Die frühere Kode-Kleinheit und Kompiliergeschwindigkeit sind überhaupt gar nicht mehr erreichbar, sondern die neuen Kompiler sind mehrfach langsamer und produzieren beträchtlich größeren Kode.
Eine Erklärung ist, daß Intel offenbar am besten seine eigenen Prozessoren kennt, und auf anderer Seite einiges falsch interpretiert wird.
Instruktionen, die früher vermieden wurden, werden heute (im falschen Glauben) verwendet. Beispielsweise ausgiebig setccc. Diese hat eine Latency von 5 auf P4, nicht 1/2, wie die klassischen Alternativen.
Triumphierend wurde ich belehrt, daß man Teilregister auf P4 auf gar keinen Fall verwenden sollte.
Das Gegenteil ist der Fall!:
Die Penalty-Clocks (Straftakte) des PIII bei Benutzung von Teilregistern mit
gleichzeitiger Abhängigkeit, sind beim P4 verschwunden!
Und zwar, weil bei P4 immer das ganze Register als benutzt markiert wird.
Es gibt jetzt lediglich eine Parallelitätsverhinderung durch sogenannte falsche Abhängigkeiten bei Instruktionsketten, die fortlaufend Teile ein und desselben ganzen Registers benutzen. Eingestreute Vollzugriffe können dabei einen gewissen Gewinn durch Splitten in Gruppen bringen.
Jedoch Parallelität wurde auch bei PIII schon verhindert bei solchen Abhängigkeiten.
Der P4 wurde also hinsichtlich der Verwendung von »al,ah, bl,bh, cl,ch, dl,dh« stark
verbessert!
8 BYTE-Register + 4 DWORD-Register sind allemal besser - dies wird jedoch
akribisch vermieden, besonders bei den gcc-Versionen, die immer langsameren Kode
erzeugen.
Vom Quellkode der bsh her verwendet icc übrigens ~170-mal Teilregister [abcd]h,
der gcc 2.95 ~30-mal, gcc 4.0 ~25-mal.
icc verwendet setccc ~70-mal, gcc 4.0 ~600-mal.
([abcd]l werden natürlich viel häufiger verwendet.)
icc verwendet ~2600-mal inc/dec, gcc 2.95 ~1300-mal, gcc4 ~1100-mal.
cmp op, $0 wird statt or op, op verwendet, um Z- und S-Flag zu setzen:
gcc 2.95 ~1130-mal, gcc4 ~560-mal, icc ~254-mal.
Es werden die traditionellen Performance-Tricks immer mehr vermieden.
Es werden kleine Einzelobjekte auch nicht im Stack über große Puffer gelegt,
um große Displacement-Werte zu vermeiden.
Tja, da liegen wohl die Gründe ...
Früher hatte ein Compiler-lauf 4s gedauert, heute 20s - und produziert langsameren Kode.
Die Moral von der Geschichte (wie immer): "Glaube bei der Optimierung von Code nichts und niemanden, nur dem was du selbst vermessen und verifiziert hast!". -- HelmutLeitner
Klar. Beim gcc4.2 dauert ein Compiler-lauf 48s. Aber er dürfte jetzt mindestens so schnellen Code produzieren wie icc8.1, bei meinem Projekt-Konzept:
Empfehlungen:
-mtune=zielcpu (-march=zielcpu weglassen!) -O1 -fregmove -malign-double -falign-loops=4 -falign-jumps=4 -falign-functions=16 -funit-at-a-time -fomit-frame-pointer -freorder-blocks -freorder-blocks-and-partition -freorder-functionsAlles static und in die Hauptdatei alle weiteren xyz.c inkludieren.
gcc -fprofile-generate Programmlauf gcc -fprofile-useDie Instruktionen, die durch -march verwendet werden (z.B. cmovccc), sind offenbar langsamer als die klassischen Alternativ-codierungen.
Gcc4.2 Code1 ist mit Profiling etwa 2.5% schneller als icc8.1 Code1 mit Profiling.
Gcc4.2 Code2 ist mit Profiling etwa 7% langsamer als icc8.1 Code2 ohne Profiling.
Gcc4.2 Code2 ist mit Profiling etwa 30% langsamer als icc8.1 Code2 mit Profiling.
Nach Modifikation des Code2 ist wiederum gcc4.2 um ~3% schneller.
Der icc kompiliert vielfach schneller als der neueste gcc: 12s statt 48s.
In der Summe (Code-Speed) ist der neueste gcc fast gleichzusetzen mit dem etwas älteren icc. Wenn z.B. long long in's Spiel kommt und/oder bei kleineren Programmen, scheint icc deutlich besser.
Allerdings gibt es inzwischen icc9.0 und icc9.1, wobei unklar ist, ob die (noch) besser optimieren. icc bedient nur iX86 und Itanium.
Links |