In C # gibt es signifikante Leistungsunterschiede bei der Verwendung von UInt32 vs Int32

8

Ich portiere eine bestehende Anwendung nach C # und möchte die Leistung wo immer möglich verbessern. Viele vorhandene Schleifenzähler und Array-Verweise sind als System.UInt32 anstelle von Int32 definiert, die ich verwendet hätte.

Gibt es signifikante Leistungsunterschiede bei der Verwendung von UInt32 gegenüber Int32?

    
Noah 20.11.2008, 19:50
quelle

7 Antworten

10

Ich glaube nicht, dass es irgendwelche Leistungsüberlegungen gibt, abgesehen von möglichen Unterschieden zwischen vorzeichenbehafteten und vorzeichenlosen Berechnungen auf der Prozessor-Ebene, aber zu diesem Zeitpunkt denke ich, dass die Unterschiede nicht stimmen.

Der größere Unterschied liegt in der CLS-Konformität, da die nicht signierten Typen nicht CLS-kompatibel sind, da sie nicht von allen Sprachen unterstützt werden.

    
Scott Dorman 20.11.2008, 19:56
quelle
13

Die kurze Antwort ist "Nein. Jede Auswirkung auf die Leistung wird vernachlässigbar sein".

Die richtige Antwort ist "Es kommt darauf an."

Eine bessere Frage ist: "Soll ich Uint benutzen, wenn ich sicher bin, dass ich kein Zeichen brauche?"

Der Grund, warum Sie kein definitives "Ja" oder "Nein" in Bezug auf die Leistung geben können, liegt darin, dass die Zielplattform letztlich die Leistung bestimmt. Das heißt, die Leistung wird von jedem Prozessor bestimmt, der den Code ausführt, und den verfügbaren Anweisungen. Ihr .NET-Code kompiliert bis Intermediate Language (IL oder Bytecode). Diese Anweisungen werden dann von Just-In-Time (JIT) Compiler als Teil der Common Language Runtime (CLR). Sie können nicht kontrollieren oder vorhersagen, welcher Code für jeden Benutzer generiert wird.

Wenn Sie also wissen, dass die Hardware der letzte Leistungsverteiler ist, lautet die Frage: "Wie unterschiedlich ist der Code, den .NET für eine vorzeichenbehaftete oder vorzeichenlose Ganzzahl generiert?" und "Hat der Unterschied Auswirkungen auf meine Anwendung und meine Zielplattformen?"

Der beste Weg, diese Fragen zu beantworten, besteht darin, einen Test durchzuführen.

%Vor%

Die beiden Testmethoden TestSigned und TestUnsigned führen jeweils ~ 2 Millionen Iterationen eines einfachen Inkrements für eine Ganzzahl mit und ohne Vorzeichen durch. Der Testcode führt 100 Iterationen jedes Tests durch und mittelt die Ergebnisse. Dies sollte mögliche Inkonsistenzen ausschließen. Die Ergebnisse auf meinem i7-5960X für x64 kompiliert wurden:

%Vor%

Diese Ergebnisse sind fast identisch, aber um eine definitive Antwort zu erhalten, müssen wir uns wirklich den Bytecode anschauen, der für das Programm generiert wurde. Wir können ILDASM als Teil der .NET SDK zum Überprüfen des Codes in der Assembly, die vom Compiler generiert wurde.

Hier sehen wir, dass der C # -Compiler vorzeichenbehaftete Ganzzahlen bevorzugt und die meisten Operationen nativ als Ganzzahlen mit Vorzeichen ausführt und den Wert im Speicher immer nur als vorzeichenlos behandelt, wenn er für die Verzweigung vergleicht (a.k.a jump oder if). Trotz der Tatsache, dass wir eine Ganzzahl ohne Vorzeichen sowohl für den Iterator als auch für den Akkumulator in TestUnsigned verwenden, ist der Code fast identisch mit der TestSigned -Methode mit Ausnahme einer einzigen Anweisung: IL_0016 . Ein kurzer Blick auf die ECMA-Spezifikation beschreibt den Unterschied:

  

blt.un.s:   Verzweigen zum Ziel wenn weniger als (unsigned oder ungeordnet), kurze Form.

     

blt.s:   Verzweigen zum Ziel wenn weniger als, kurze Form.

Da es sich um eine solche allgemeine Anweisung handelt, kann davon ausgegangen werden, dass die meisten modernen Hochleistungsprozessoren Hardwareanweisungen für beide Operationen haben und sehr wahrscheinlich in der gleichen Anzahl von Zyklen ausgeführt werden, aber das ist nicht garantiert . Ein Low-Power-Prozessor möglicherweise weniger Anweisungen und keine Verzweigung für unsigned Int. In diesem Fall muss der JIT-Compiler möglicherweise mehrere Hardwareanweisungen ausgeben (zuerst eine Konvertierung, dann eine Verzweigung), um die IL-Anweisung blt.un.s auszuführen. Selbst wenn dies der Fall ist, wären diese zusätzlichen Anweisungen grundlegend und würden wahrscheinlich die Leistung nicht wesentlich beeinträchtigen.

Was die Leistung anbelangt, lautet die lange Antwort: "Es ist unwahrscheinlich, dass es überhaupt einen Leistungsunterschied zwischen der Verwendung einer Ganzzahl mit Vorzeichen oder ohne Vorzeichen geben wird. Wenn es einen Unterschied gibt, ist es wahrscheinlich vernachlässigbar."

Wenn also die Leistung identisch ist, lautet die nächste logische Frage: "Soll ich einen Wert ohne Vorzeichen verwenden, wenn ich sicher bin, dass ich kein Zeichen brauche?"

Es gibt zwei Dinge, die hier zu beachten sind: erstens, vorzeichenlose Ganzzahlen sind NICHT CLS-kompatibel , was bedeutet, dass Probleme auftreten können, wenn Sie eine vorzeichenlose Ganzzahl als Teil einer API offenlegen, die von einem anderen Programm verwendet wird (z. B. wenn Sie eine wiederverwendbare Bibliothek verteilen). Zweitens verwenden die meisten Operationen in .NET, einschließlich der Methodensignaturen, die von der BCL bereitgestellt werden (aus dem obigen Grund), eine Ganzzahl mit Vorzeichen. Wenn Sie also Ihre nicht vorzeichenbehaftete Ganzzahl verwenden möchten, werden Sie wahrscheinlich feststellen, dass Sie sie ziemlich oft umgesetzt haben. Dies wird einen sehr kleinen Leistungseinbruch haben und Ihren Code etwas unordentlicher machen. Am Ende ist es wahrscheinlich nicht wert.

TLDR; In meinen C ++ - Tagen würde ich sagen: "Verwenden Sie das, was am besten passt, und lassen Sie den Compiler den Rest sortieren." C # ist nicht ganz so cut-and-dry, also würde ich das für .NET sagen: Es gibt wirklich keinen Leistungsunterschied zwischen einer vorzeichenbehafteten und vorzeichenlosen Ganzzahl auf x86 / x64, aber die meisten Operationen erfordern eine vorzeichenbehaftete Ganzzahl, es sei denn, Sie brauchen das wirklich schränken Sie die Werte NUR auf positiv ein oder Sie BENÖTIGEN wirklich den zusätzlichen Bereich, den das Vorzeichenbit ißt, bleiben Sie bei einer vorzeichenbehafteten Ganzzahl. Dein Code wird am Ende sauberer sein.

    
Robear 02.02.2016 00:16
quelle
3

Ich habe in .NET keine Forschung zu diesem Thema gemacht, aber in den alten Tagen von Win32 / C ++, wenn Sie ein "signed int" in ein "signed long" umwandeln wollten, musste die CPU ein op um das Zeichen zu verlängern. Um ein "unsigned int" in ein "unsigned long" zu schreiben, hatte es in den oberen Bytes nur stuff null. Einsparungen waren in der Größenordnung von ein paar Taktzyklen (d. H. Sie müssten es Milliarden von Malen tun, um eine sogar wahrnehmbare Differenz zu haben)

    
James Curran 20.11.2008 19:59
quelle
3

Es gibt keinen Unterschied, leistungsmäßig. Einfache Ganzzahlberechnungen sind gut bekannt und moderne CPUs sind stark optimiert, um sie schnell auszuführen.

    
Alan 20.11.2008 20:01
quelle
1

Diese Arten von Optimierungen sind den Aufwand selten wert. Verwenden Sie den Datentyp, der für die Aufgabe am besten geeignet ist, und belassen Sie ihn dabei. Wenn dieses Ding so sehr eine Datenbank berührt, könnte man wahrscheinlich ein Dutzend Verbesserungen im DB-Design, in der Abfragesyntax oder der Indizierungsstrategie finden, die eine Codeoptimierung in C # um einige hundert Größenordnungen ausgleichen würden.

    
JohnFx 20.11.2008 22:30
quelle
0

Es wird die gleiche Menge an Speicher auf die gleiche Weise zugewiesen (obwohl die eine einen größeren Wert speichern kann, da sie keinen Speicherplatz für das Zeichen speichert). Daher bezweifle ich, dass Sie einen "Leistungsunterschied" sehen, es sei denn, Sie verwenden große Werte / negative Werte, die dazu führen, dass die eine oder die andere Option explodiert.

    
GWLlosa 20.11.2008 19:54
quelle
0

Das hat nicht wirklich etwas mit Performance-Anforderungen für den Schleifenzähler zu tun.

Vielleicht gab es viele Iterationen zu vervollständigen

%Vor%

Der unsigned int kann aus einem bestimmten Grund da sein.

    
Rohan West 20.11.2008 20:07
quelle

Tags und Links