Ich habe einen zusammengesetzten Indextyp, der aus zwei 16-Bit-Ganzzahlen besteht, die zu einem 32-Bit-Objekt gepackt sind, das so entworfen wurde, dass es herumgereicht und ein wenig wie ein Zeiger behandelt wird. Aber ich bemerkte zufällig, dass die von mir definierten Vergleichsoperatoren nicht so optimiert wurden, wie ich es erwartet hatte.
Gegeben dieser Kürzel:
%Vor% GCC (7.1 auf Compiler-Explorer ) erzeugt die folgende Assembly (Optionen -m64 -std=c++11 -O3
):
Einer davon scheint mehr zu tun als der andere. Aber ich kann einfach nicht sehen, wie sie sich unterscheiden: Die Behauptung garantiert, dass das Layout der Struktur so ist, dass der Vergleich als uint23_t
alle gleichen Daten vergleichen muss wie die co_de% Felder getrennt. Noch wichtiger ist, dass dies x86 ist, also wusste der Compiler bereits, dass dies der Fall sein würde. Das Kurzschlußverhalten von uint16_t
sollte für die Ausgabe nicht wichtig sein, da sein rechter Operand keine Auswirkungen hat (und der Compiler kann dies sehen), und da nichts anderes interessant ist, kann ich mir nicht vorstellen warum würde zB wollen verzögert das Laden der zweiten Hälfte der Daten.
Das Ersetzen von &&
durch den Operator &&
beseitigt den Sprung, ändert aber nicht grundlegend, was der Code tut: Er generiert immer noch zwei separate 16-Bit-Vergleiche, anstatt alle Daten auf einmal zu vergleichen. was darauf hindeutet, dass der Kurzschluss wahrscheinlich nicht das Problem ist (obwohl es eine verwandte Frage aufwirft, warum es &
und &&
auch nicht auf die gleiche Weise kompiliert - sicherlich sollte einer der beiden "besser" sein in beiden Fällen).
Was mich interessiert, ist, dass laut Compiler Explorer alle großen Compiler (GCC, Clang, Intel, MSVC) ungefähr dasselbe tun. Das reduziert die Wahrscheinlichkeit, dass dies ein Versehen seitens eines Optimierers ist, aber was ich nicht sehen kann ist, wie meine eigene Einschätzung dies tatsächlich falsch ist.
Es gibt also zwei Teile:
1) macht &
wirklich dasselbe wie equal1
? Fehle ich etwas verrückt hier?
2) Wenn dies der Fall ist, warum sollten Compiler die kürzere Befehlssequenz nicht ausgeben?
Ich bin mir sicher, dass Optimierungen in diesem Sinne etwas sein müssen, über das Compiler Bescheid wissen, weil sie legitimer nützlich wären, um anderen, ernsteren Code wie z. equal2
schiebt Dinge in Vektorregister, um mehr Daten gleichzeitig zu vergleichen.
Tags und Links optimization c++ assembly