In C # gibt es einen signifikanten Performance-Unterschied für die Verwendung von UInt32 vs Int32

StackOverflow https://stackoverflow.com/questions/306602

  •  08-07-2019
  •  | 
  •  

Frage

Ich bin die Portierung einer bestehenden Anwendung in C # und wollen die Leistung verbessern, wo immer möglich. Viele bestehende Schleifenzähler und Array-Referenzen werden als System.UInt32 definiert, anstelle des Int32 würde ich verwendet habe.

Gibt es einen signifikanten Unterschied in der Leistung für die Verwendung von UInt32 vs Int32?

War es hilfreich?

Lösung

Ich glaube nicht, gibt es keine Leistungsinformationen, andere als mögliche Differenz zwischen mit und ohne Vorzeichen Arithmetik auf Prozessorebene, aber an diesem Punkt Ich denke, die Unterschiede strittig sind.

Je größer ist der Unterschied in der CLS-Kompatibilität, da die unsigned Typen nicht CLS-kompatibel sind, da nicht alle Sprachen sie unterstützen.

Andere Tipps

Die kurze Antwort ist „Nein. Alle Auswirkungen auf die Leistung vernachlässigbar ist“.

Die richtige Antwort lautet: "Es hängt davon ab."

Eine bessere Frage ist: „Soll ich uint verwenden, wenn ich sicher bin, brauche ich nicht ein Zeichen?“

Der Grund, warum Sie können keine endgültige „Ja“ oder „Nein“ in Bezug auf die Leistung geben wird, weil die Zielplattform wird letztlich Leistung bestimmen. Das heißt, die Leistung diktiert, was auch immer Prozessor wird den Code zu Ausführung und die Anweisungen zur Verfügung. Ihre .NET-Code kompiliert bis auf Intermediate Language (IL oder Bytecode). Diese Befehle werden dann auf die Zielplattform kompiliert durch die Just-In-Time (JIT) Compiler als Teil des Common Language Runtime (CLR). Sie können steuern, oder nicht vorhersagen, welcher Code werden für jeden Benutzer erzeugt werden.

So wohl wissend, dass die Hardware die letzte Instanz der Leistung ist, wird die Frage: „Wie anders ist der Code .NET für einen im Vergleich zu unsigned integer unterzeichnet erzeugt?“ und „Hat sich dieser Unterschied auf meine Bewerbung und mein Zielplattformen?“

Der beste Weg, um diese Fragen zu beantworten, ist ein Test ausgeführt werden.

class Program
{
  static void Main(string[] args)
  {
    const int iterations = 100;
    Console.WriteLine($"Signed:      {Iterate(TestSigned, iterations)}");
    Console.WriteLine($"Unsigned:    {Iterate(TestUnsigned, iterations)}");
    Console.Read();
  }

  private static void TestUnsigned()
  {
    uint accumulator = 0;
    var max = (uint)Int32.MaxValue;
    for (uint i = 0; i < max; i++) ++accumulator;
  }

  static void TestSigned()
  {
    int accumulator = 0;
    var max = Int32.MaxValue;
    for (int i = 0; i < max; i++) ++accumulator;
  }

  static TimeSpan Iterate(Action action, int count)
  {
    var elapsed = TimeSpan.Zero;
    for (int i = 0; i < count; i++)
      elapsed += Time(action);
    return new TimeSpan(elapsed.Ticks / count);
  }

  static TimeSpan Time(Action action)
  {
    var sw = new Stopwatch();
    sw.Start();
    action();
    sw.Stop();
    return sw.Elapsed;
  }
}

Die beiden Testmethoden, TestSigned und TestUnsigned , führen jeweils ~ 2.000.000 Iterationen eines einfachen Schritt auf einem mit und ohne Vorzeichen integer sind. Der Testcode läuft 100 Iterationen der einzelnen Tests und mittelt die Ergebnisse. Dies sollte mögliche Inkonsistenzen auszusondern. Die Ergebnisse auf meinem i7-5960X für x64 kompiliert waren:

Signed:      00:00:00.5066966

Unsigned:    00:00:00.5052279

Diese Ergebnisse fast identisch sind, aber eine endgültige Antwort zu erhalten, müssen wir wirklich an der Bytecode für das Programm erzeugt suchen. Wir können verwenden ILDASM als Teil des .NET SDK den Code in der Montage durch den Compiler.

 Bytecode

siehe

Hier können wir, dass die C # -Compiler favorisiert ganzen Zahlen unterzeichnet und führt tatsächlich die meisten Operationen nativ als signierte ganze Zahlen sind und immer nur behandelt den Wert im Speicher als unsigned, wenn für die Branche zu vergleichen (a.k.a Sprung oder if). Trotz der Tatsache, dass wir verwenden eine ganze Zahl ohne Vorzeichen sowohl für den Iterator und den Speicher in TestUnsigned , der Code ist fast identisch mit der TestSigned Verfahren mit Ausnahme einer einzigen Anweisung: IL_0016 . Ein kurzer Blick auf die ECMA spec beschreibt den Unterschied:

  

blt.un.s:   Zweig zum Ziel, wenn weniger als (unsigned oder ungeordnet), Kurzform.

     

blt.s:   Zweig zum Ziel, wenn weniger als, Kurzform.

Als eine solche gemeinsame Anweisung, dann ist es sicher davon ausgehen, dass die meisten modernen High-Power-Prozessoren Hardware-Anweisungen für beide Operationen haben wird, und sie werden sehr wahrscheinlich in der gleichen Anzahl von Zyklen ausführen, aber dies nicht gewährleistet . Ein Low-Power-Prozessor kann wenige Befehle hat und nicht einen Zweig für unsigned int hat. In diesem Fall kann der JIT-Compiler, um mehrere Hardware-Befehle (A-Wandlung zuerst, dann eine Verzweigung, zum Beispiel) auszuführen, um die blt.un.s IL Anweisung auszusenden. Selbst wenn dies der Fall ist, würde diese zusätzlichen Anweisungen basisch sein und wahrscheinlich würde die Leistung nicht wesentlich auswirken.

So in Bezug auf performance, die lange Antwort ist: „Es ist unwahrscheinlich, dass es überhaupt einen Unterschied in der Leistung sein, ein signiertes zwischen der Verwendung oder eine ganze Zahl ohne Vorzeichen. Wenn es einen Unterschied gibt, ist es wahrscheinlich vernachlässigbar sein.“

Also dann, wenn die Leistung identisch ist, ist die nächste logische Frage: „Soll ich einen Wert ohne Vorzeichen verwenden, wenn ich bin sicher, dass ich nicht Notwendigkeit ein Zeichen?“

Es gibt zwei Dinge hier zu betrachten: Erstens, ganze Zahlen ohne Vorzeichen sind NICHT CLS-kompatibel , was bedeutet, dass Sie auf Probleme stoßen, wenn Sie eine ganze Zahl ohne Vorzeichen als Teil einer API ist aussetzt, die ein anderes Programm verbrauchen (wie wenn Sie eine Verteilung wiederverwendbare Bibliothek). Zweitens, die meisten Operationen in .NET, einschließlich den Methodensignaturen vom BCL Exposed (aus dem Grunde, oben), verwenden, um eine Ganzzahl mit Vorzeichen. Also, wenn Sie auf tatsächlich mit Ihrem unsigned integer planen, werden Sie wahrscheinlich finden sie es durchaus ein wenig zu werfen. Das wird eine sehr kleine Leistung getroffen haben und Ihren Code ein wenig unübersichtlicher machen. Am Ende ist es wahrscheinlich nicht wert.

TLDR; zurück in meinen C ++ Tagen, würde ich sagen "Verwenden Sie, was am besten geeignet ist und lassen Sie die Compiler Art den Rest aus" C # ist nicht ganz so eingeschnittenen und trocken, so würde ich sagen für .NET: Es gibt wirklich keinen Unterschied in der Leistung zwischen einer und ohne Vorzeichen Ganzzahl auf x86 / x64, aber die meisten Operationen erfordern eine signierte ganze Zahl ist, so dass, wenn Sie wirklich brauchen, um schränkt die Werte nur positiv oder Sie müssen wirklich den extra Bereich, dass das Vorzeichenbit isst, Stick mit einer Ganzzahl mit Vorzeichen. Ihr Code wird am Ende sauberer sein.

Ich habe keine Forschung über die Angelegenheit in .NET getan, aber in den alten Tagen von Win32 / C ++, wenn Sie ein „signed int“ zu einem „unterzeichnet lange“, die CPU mußte läuft ein werfen wollten op das Zeichen zu verlängern. Um wirft ein „unsigned int“ auf einen „unsigned long“, hatte es nur Sachen Null in dem oberen Bytes. Einsparungen in der Größenordnung von ein paar Taktzyklen waren (das heißt, würden Sie ihm Milliarden von Zeiten zu tun haben, einen noch wahrnehmbaren Unterschied haben)

Es gibt keinen Unterschied, Leistung klug. Einfache integer Berechnungen sind gut bekannt und modernen CPUs sind sie hoch optimierte auszuführen schnell.

Diese Arten von Optimierungen sind selten die Mühe wert. Verwenden Sie den Datentyp, der am besten geeignet für die Aufgabe ist und belassen es dabei. Wenn diese Sache so viel wie eine Datenbank berührt könnte man wahrscheinlich ein Dutzend Verbesserungen in der DB-Design, Abfragesyntax oder Indizierung Strategie finden, die eine Code-Optimierung in C # durch ein paar hundert Größenordnung abdecken würden.

Sein Gehen die gleiche Menge an Speicher oder so zuzuordnen (obwohl der man einen größeren Wert speichern kann, als nicht Platz für das Zeichen zu speichern). Also ich bezweifle es eine ‚Leistung‘ Unterschied sehen, es sei denn, Sie große Werte / negative Werte verwenden, die eine Option oder die andere zur Explosion führen wird.

Das ist wirklich nicht mit Leistung eher Anforderungen an den Schleifenzähler zu tun.

Prehaps es gab viele Iterationen zu beenden

        Console.WriteLine(Int32.MaxValue);      // Max interation 2147483647
        Console.WriteLine(UInt32.MaxValue);     // Max interation 4294967295

Der unsigned int aus einem Grund da sein kann.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top