< schneller <= in C++? Mythen entlarven mit Beispielen

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Ein Mythos, der seit Jahrzehnten lebt

\n\n

Wenn Sie jemals auf Stack Overflow im Bereich C++ unterwegs waren, haben Sie sicherlich die Frage gesehen: „Is < faster than <=?“. Diese Frage hat tausende Aufrufe und Dutzende Diskussionsseiten gesammelt. Und das ist kein Wunder – das Thema Optimierung von Code bewegt die Gemüter von Programmierern aller Erfahrungsstufen.

\n\n

Die Logik scheint bombenfest: Der Operator < vergleicht zwei Zahlen und gibt ein Ergebnis zurück, während <= dasselbe tut, aber zusätzlich auf Gleichheit prüft. Also muss < schneller sein, oder? Besonders wichtig ist dies für Schleifen, wo die Vergleichsoperation millionenfach ausgeführt wird.

\n\n

Heute werden wir diesen Mythos ein für alle Mal klären. Sie erfahren:

\n
    \n
  • Wie die Operatoren auf Prozessorebene tatsächlich arbeiten.
  • \n
  • Warum der Compiler Ihr bester Freund (oder Feind, wenn Sie ihm nicht vertrauen) ist.
  • \n
  • Wann Mikrooptimierung sinnvoll ist und wann sie Zeitverschwendung ist.
  • \n
\n\n

Wie arbeiten die Operatoren < und <= auf Assembler-Ebene?

\n\n

Um zu verstehen, ob es einen Unterschied gibt, müssen wir „unter die Haube“ schauen – in das, was der Compiler aus Ihrem C++-Code macht. Betrachten wir zwei einfache Funktionen:

\n\n
bool less_than(int a, int b) {\n    return a < b;\n}\n\nbool less_or_equal(int a, int b) {\n    return a <= b;\n}
\n\n

Kompilieren wir sie mit g++ (ohne Optimierungen, Flag -O0). Folgendes sehen wir im Assembler-Listing (x86-64):

\n\n
less_than(int, int):\n    cmp    edi, esi      ; a und b vergleichen\n    setl   al            ; al=1 setzen wenn a < b (kleiner)\n    ret\n\nless_or_equal(int, int):\n    cmp    edi, esi      ; a und b vergleichen\n    setle  al            ; al=1 setzen wenn a <= b (kleiner oder gleich)\n    ret
\n\n

Sehen Sie? Der einzige Unterschied sind die Befehle setl (set if less) und setle (set if less or equal). Beide Befehle werden in einem Prozessortakt ausgeführt. Auf modernen CPUs (Intel Core i7, AMD Ryzen) haben sie dieselbe Latenz – 1 Zyklus.

\n\n

Fazit Nr. 1: Auf Maschinenbefehlsebene gibt es keinen Leistungsunterschied zwischen < und <=. Punkt.

\n\n

Und was ist mit Schleifen? Hier muss es doch einen Unterschied geben!

\n\n

Meistens entbrennt die Debatte um Schleifen. Vergleichen Sie zwei Ansätze:

\n\n
// Variante 1: mit <\nfor (int i = 0; i < n; ++i) {\n    // etwas tun\n}\n\n// Variante 2: mit <=\nfor (int i = 0; i <= n - 1; ++i) {\n    // dasselbe tun\n}
\n\n

Die Intuition sagt: Die zweite Variante macht eine überflüssige Berechnung n - 1. Aber schauen wir, was nach der Compiler-Optimierung passiert.

\n\n

Starten wir einen Benchmark mit g++ -O2 für n = 1 000 000 000:

\n\n
#include <iostream>\n#include <chrono>\n\nint main() {\n    const long long n = 1'000'000'000;\n    volatile long long sink = 0; // damit der Compiler die Schleife nicht wegwirft\n\n    auto start = std::chrono::high_resolution_clock::now();\n    \n    for (long long i = 0; i < n; ++i) {\n        sink += i;\n    }\n    \n    auto end = std::chrono::high_resolution_clock::now();\n    std::cout << "Zeit (<): " \n              << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()\n              << " ms\\n";\n\n    start = std::chrono::high_resolution_clock::now();\n    \n    for (long long i = 0; i <= n - 1; ++i) {\n        sink += i;\n    }\n    \n    end = std::chrono::high_resolution_clock::now();\n    std::cout << "Zeit (<=): " \n              << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()\n              << " ms\\n";\n\n    return 0;\n}
\n\n

Ergebnisse auf meinem Rechner (i7-107

Blogs

Buchempfehlungen