Ich habe kürzlich an einem persönlichen Projekt gearbeitet, als ich auf ein seltsames Problem gestoßen bin.
In einer sehr engen Schleife habe ich eine Ganzzahl mit einem Wert zwischen 0 und 15. Ich muss -1 für die Werte 0, 1, 8 und 9 und 1 für die Werte 4, 5, 12 und 13 erhalten.
Ich wandte mich an Godbolt, um einige Optionen zu überprüfen, und war überrascht, dass der Compiler eine switch-Anweisung nicht wie eine if-Kette optimieren konnte.
Der Link ist hier: https://godbolt.org/z/WYVBFl
Der Code lautet:
const int lookup[16] = {-1, -1, 0, 0, 1, 1, 0, 0, -1, -1, 0, 0, 1, 1, 0, 0};
int a(int num) {
return lookup[num & 0xF];
}
int b(int num) {
num &= 0xF;
if (num == 0 || num == 1 || num == 8 || num == 9)
return -1;
if (num == 4 || num == 5 || num == 12 || num == 13)
return 1;
return 0;
}
int c(int num) {
num &= 0xF;
switch (num) {
case 0: case 1: case 8: case 9:
return -1;
case 4: case 5: case 12: case 13:
return 1;
default:
return 0;
}
}
Ich hätte gedacht, dass b und c die gleichen Ergebnisse liefern würden, und ich hatte gehofft, dass ich die Bit-Hacks lesen könnte, um selbst eine effiziente Implementierung zu erzielen, da meine Lösung (die switch-Anweisung - in einer anderen Form) ziemlich langsam war.
Seltsamerweise wurde b
es zu Bit-Hacks kompiliert, während c
es entweder ziemlich unoptimiert war oder auf einen anderen Fall reduziert wurde, a
abhängig von der Zielhardware.
Kann jemand erklären, warum es diese Diskrepanz gibt? Was ist der "richtige" Weg, um diese Abfrage zu optimieren?
BEARBEITEN:
Klärung
Ich möchte, dass die Switch-Lösung die schnellste oder eine ähnlich "saubere" Lösung ist. Beim Kompilieren mit Optimierungen auf meinem Computer ist die if-Lösung jedoch erheblich schneller.
Ich habe ein schnelles Programm geschrieben, um zu demonstrieren, und TIO hat die gleichen Ergebnisse wie vor Ort: Probieren Sie es online aus!
Mit static inline
der Nachschlagetabelle etwas schneller : Probieren Sie es online aus!
if
schlägt aber immer noch switch
(seltsamerweise wird die Suche noch schneller) [TIO folgt]
-O3
kompiliert, und es wurdec
zu etwas kompiliert , das wahrscheinlich schlimmer ist alsa
oderb
(c
hatte zwei bedingte Sprünge plus ein paar Bitmanipulationen im Vergleich zu nur einem bedingten Sprung und einer einfacheren Bitmanipulation fürb
), aber immer noch besser als naive Item-by-Item-Tests. Ich bin mir nicht sicher, wonach Sie hier wirklich fragen. Die einfache Tatsache ist, dass ein optimierender Compiler jedes dieser Elemente in eines der anderen umwandeln kann, wenn er dies wünscht, und es gibt keine festen Regeln dafür, was er tun oder nicht tun wird.