Leistung von vorzeichenlosen vs. vorzeichenbehafteten Ganzzahlen

Lesezeit: 7 Minuten

Gibt es einen Leistungsgewinn/-verlust durch die Verwendung von vorzeichenlosen Ganzzahlen gegenüber vorzeichenbehafteten Ganzzahlen?

Wenn ja, gilt das auch für kurz und lang?

  • @JeremyP, darf ich vorschlagen, dass Sie nur für die Mehrheit der Entwickler und Anwendungen die Wahrheit gesagt haben ….

    – Brett

    11. November 12 um 21:31 Uhr

  • @Brett: Der Unterschied zwischen vorzeichenbehafteter und vorzeichenloser Arithmetik auf den meisten CPUs ist Null. Der Unterschied für verschiedene Größen ist marginal, wenn Sie nicht viel rechnen.

    – JeremyP

    12. November 12 um 8:03 Uhr

Division durch Potenzen von 2 ist schneller mit unsigned int, weil es in eine einzelne Verschiebungsanweisung optimiert werden kann. Mit signed int, erfordert es normalerweise mehr Maschinenbefehle, weil die Division rundet gegen null, aber Verschiebung nach rechts rundet Nieder. Beispiel:

int foo(int x, unsigned y)
{
    x /= 8;
    y /= 8;
    return x + y;
}

Hier ist das Relevante x Teil (signierte Teilung):

movl 8(%ebp), %eax
leal 7(%eax), %edx
testl %eax, %eax
cmovs %edx, %eax
sarl $3, %eax

Und hier ist das Relevante y Teil (Teilung ohne Vorzeichen):

movl 12(%ebp), %edx
shrl $3, %edx

  • Dies funktioniert nur, wenn der Divisor eine zur Kompilierzeit bekannte Konstante ist, die eine Zweierpotenz ist, nicht wahr?

    – scharfer Zahn

    17. Januar 11 um 11:31 Uhr

  • @sharptooth, für die Division, ja. Es gibt wahrscheinlich noch andere Bit-Manipulationstricks, die nur für unsigned gelten. Oder signiert. Ich glaube nicht, dass der positive Effekt nur in eine Richtung geht.

    – Ein Programmierer

    17. Januar 11 um 12:06 Uhr

  • Warum funktioniert der Trick nicht für nicht konstante Teiler? Der erste Operand von x86 shrl sollte ein wörtlich sein?

    – Manu343726

    30. Dezember 13 um 11:57 Uhr

  • @ Manu343726 Was ist, wenn der Divisor keine Potenz von 2 ist? (Und selbst wenn es so wäre, müssten Sie zuerst den binären Logarithmus der Zahl berechnen, bevor Sie verschieben.)

    – fredoverflow

    4. Oktober 14 um 16:08 Uhr


  • Auf dieser Skala weitere Anleitungen bedeutet nicht immer langsamer in der Laufzeit für moderne Pipeline-CPU-Architekturen. Dh ich würde immer noch eine Messung machen, bevor ich zu weitreichenden Schlüssen komme.

    – ulidtko

    13. Juni 16 um 7:22 Uhr

In C++ (und C) ist der Überlauf von vorzeichenbehafteten Ganzzahlen undefiniert, während der Überlauf von vorzeichenlosen Ganzzahlen so definiert ist, dass er herumläuft. Beachten Sie, dass Sie z. B. in gcc das Flag -fwrapv verwenden können, um einen signierten Überlauf zu definieren (zum Umbrechen).

Undefinierter vorzeichenbehafteter Ganzzahlüberlauf lässt den Compiler davon ausgehen, dass keine Überläufe auftreten, was zu Optimierungsmöglichkeiten führen kann. Siehe zB diesen Blogbeitrag zur Diskussion.

Leistung von vorzeichenlosen vs vorzeichenbehafteten Ganzzahlen
anatolyg

unsigned führt zu gleicher oder besserer Leistung als signed. Einige Beispiele:

  • Division durch eine Konstante, die eine Potenz von 2 ist (siehe auch die Antwort von FredÜberlauf)
  • Division durch eine konstante Zahl (z. B. implementiert mein Compiler die Division durch 13 mit 2 asm-Anweisungen für unsigned und 6 Anweisungen für signed)
  • Prüfen, ob eine Zahl gerade ist (ich habe keine Ahnung, warum mein MS Visual Studio-Compiler sie mit 4 Anweisungen für implementiert signed Zahlen; gcc macht es mit 1 Anweisung, genau wie in der unsigned Fall)

short führt in der Regel zu gleicher oder schlechterer Leistung als int (vorausgesetzt sizeof(short) < sizeof(int)). Leistungseinbußen treten auf, wenn Sie ein Ergebnis einer arithmetischen Operation zuweisen (was normalerweise int, noch nie short) zu einer Variablen vom Typ short, das im Register des Prozessors gespeichert ist (ebenfalls vom Typ int). Alle Konvertierungen von short zu int dauern und sind nervig.

Hinweis: Einige DSPs haben schnelle Multiplikationsanweisungen für die signed short Typ; in diesem speziellen Fall short ist schneller als int.

Was den Unterschied zwischen int und long, kann ich nur vermuten (ich bin mit 64-Bit-Architekturen nicht vertraut). Natürlich, wenn int und long dieselbe Größe haben (auf 32-Bit-Plattformen), ist auch ihre Leistung dieselbe.


Eine sehr wichtige Ergänzung, auf die von mehreren Personen hingewiesen wurde:

Was für die meisten Anwendungen wirklich zählt, ist der Speicherbedarf und die genutzte Bandbreite. Sie sollten die kleinsten notwendigen ganzen Zahlen verwenden (short, vielleicht sogar signed/unsigned char) für große Arrays.

Dies führt zu einer besseren Leistung, aber der Gewinn ist nicht linear (dh nicht um den Faktor 2 oder 4) und etwas unvorhersehbar – er hängt von der Cache-Größe und der Beziehung zwischen Berechnungen und Speicherübertragungen in Ihrer Anwendung ab.

  • Ich wäre vorsichtig mit der Aussage über die Performance von short im Vergleich zu int. Während die Arithmetik mit int schneller sein “könnte”, sollte man bedenken, dass die Integer-Arithmetik selten ein Engpass ist (zumindest auf modernen Desktop-CPUs), die Speicherbandbreite hingegen oft, so dass bei großen Datensätzen eine kurze tatsächlich eine erheblich bessere Leistung erbringen könnte dann int. Darüber hinaus bedeutet die Verwendung kleinerer Datentypen für autovektorisierten Code häufig, dass mehr Datenelemente gleichzeitig verarbeitet werden können, sodass sogar die arithmetische Leistung steigen könnte (obwohl dies angesichts des aktuellen Stands der Autovektorisierer unwahrscheinlich ist).

    – Grizzly

    18. Januar 11 um 21:26 Uhr


  • @Grizzly Ich stimme zu (meine App ist tatsächlich rechenintensiv, also meine Erfahrung mit short ist anders als deine/jeder andere)

    – anatolyg

    19. Januar 11 um 16:59 Uhr

  • @martinkunew Absolut! Dies kann der einzige Grund für die Verwendung sein short heute (wobei Nicht-Cache-RAM praktisch unendlich ist) und das aus einem sehr guten Grund.

    – anatolyg

    30. Juni 14 um 18:55 Uhr

  • @anatolyg RAM kann effektiv unendlich sein, aber vergessen Sie nicht, dass 32-Bit-Programme immer noch 64-Bit-Programme bei weitem übertreffen, was bedeutet, dass Sie unabhängig davon, wie viel RAM verfügbar ist, oft immer noch auf 2 GB nutzbare Adresse beschränkt sind -Platz.

    – bcrist

    6. September 14 um 23:25 Uhr

  • @JoshParnell Ich denke du meinst short ist schneller als int Wenn Erinnerung gebunden. Meiner Erfahrung nach haben sie die gleiche Leistung auf x86 und short ist langsamer auf ARM.

    – anatolyg

    10. Mai 17 um 8:51 Uhr

Dies hängt von der genauen Implementierung ab. In den meisten Fällen wird es jedoch keinen Unterschied geben. Wenn es Ihnen wirklich wichtig ist, müssen Sie alle Varianten ausprobieren, die Sie in Betracht ziehen, und die Leistung messen.

Dies ist ziemlich stark vom jeweiligen Prozessor abhängig.

Auf den meisten Prozessoren gibt es Anweisungen sowohl für vorzeichenbehaftete als auch für vorzeichenlose Arithmetik, daher hängt der Unterschied zwischen der Verwendung von vorzeichenbehafteten und vorzeichenlosen Ganzzahlen davon ab, welche der Compiler verwendet.

Wenn eines der beiden schneller ist, ist es völlig prozessorspezifisch, und höchstwahrscheinlich ist der Unterschied winzig, wenn er überhaupt existiert.

1643916009 776 Leistung von vorzeichenlosen vs vorzeichenbehafteten Ganzzahlen
Gemeinschaft

Der Leistungsunterschied zwischen vorzeichenbehafteten und vorzeichenlosen Ganzzahlen ist tatsächlich allgemeiner als die Akzeptanzantwort vermuten lässt. Die Division einer vorzeichenlosen Ganzzahl durch eine beliebige Konstante kann schneller durchgeführt werden als die Division einer vorzeichenbehafteten Ganzzahl durch eine Konstante, unabhängig davon, ob die Konstante eine Zweierpotenz ist. Sehen http://ridiculousfish.com/blog/posts/labor-of-division-episode-iii.html

Am Ende seines Beitrags fügt er den folgenden Abschnitt ein:

Eine natürliche Frage ist, ob die gleiche Optimierung die vorzeichenbehaftete Division verbessern könnte; Leider scheint dies aus zwei Gründen nicht der Fall zu sein:

Die Erhöhung des Dividenden muss zu einer Erhöhung der Größe werden, dh Erhöhung, wenn n > 0, Verringerung, wenn n < 0. Dies führt zu einem zusätzlichen Aufwand.

Die Strafe für einen unkooperativen Divisor ist bei einer vorzeichenbehafteten Division nur etwa halb so hoch, wodurch ein kleineres Fenster für Verbesserungen bleibt.

Somit scheint es, dass der Abrundungsalgorithmus dazu gebracht werden könnte, in einer vorzeichenbehafteten Division zu arbeiten, aber der Standard-Aufrundungsalgorithmus unterlegen sein wird.

Nicht nur die Division durch Potenzen von 2 ist mit unsigned Type schneller, auch die Division durch alle anderen Werte ist mit unsigned Type schneller. Wenn Sie sich ansehen Agner Fogs Anleitungstabellen Sie werden sehen, dass nicht signierte Divisionen eine ähnliche oder bessere Leistung als signierte Versionen aufweisen

Zum Beispiel mit dem AMD K7

Anweisung Operanden Ops Latenz Gegenseitiger Durchsatz
DIV r8/m8 32 24 23
DIV r16/m16 47 24 23
DIV r32/m32 79 40 40
IDIV r8 41 17 17
IDIV r16 56 25 25
IDIV r32 88 41 41
IDIV m8 42 17 17
IDIV m16 57 25 25
IDIV m32 89 41 41

Dasselbe gilt für Intel Pentium

Anweisung Operanden Taktzyklen
DIV r8/m8 17
DIV r16/m16 25
DIV r32/m32 41
IDIV r8/m8 22
IDIV r16/m16 30
IDIV r32/m32 46

Natürlich sind die ziemlich alt. Neuere Architekturen mit mehr Transistoren könnten die Lücke schließen, aber die grundlegenden Dinge gelten: Im Allgemeinen benötigen Sie mehr Mikrooperationen, mehr Logik und mehr Latenz, um eine vorzeichenbehaftete Division durchzuführen

.

759160cookie-checkLeistung von vorzeichenlosen vs. vorzeichenbehafteten Ganzzahlen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy