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?
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?
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.
anatolyg
unsigned
führt zu gleicher oder besserer Leistung als signed
. Einige Beispiele:
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.
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
.
@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