Warum sollte ich in C # int anstelle eines Bytes oder Short verwenden?

73

Ich habe einige Themen zu diesem Thema gefunden. Die meisten Leute scheinen die Verwendung von int in ihrem C # -Code auf der ganzen Linie zu bevorzugen, selbst wenn ein Byte oder Smallint die Daten verarbeiten würde, es sei denn, es handelt sich um eine mobile App. Ich verstehe nicht warum. Ist es nicht sinnvoller, Ihren C # -Datentyp als denselben Datentyp zu definieren, der in Ihrer Datenspeicherlösung enthalten wäre?

Meine Prämisse: Wenn ich ein typisiertes Dataset, Linq2SQL-Klassen, POCO auf die eine oder andere Weise verwende, treten Probleme bei der Konvertierung von Compiler-Datentypen auf, wenn ich meine Datentypen nicht über meine Ebenen hinweg synchron halte. Ich mache System.Convert nicht immer gerne, nur weil es einfacher war, int auf der ganzen Linie in C # -Code zu verwenden. Ich habe immer den kleinsten Datentyp verwendet, der benötigt wird, um die Daten in der Datenbank sowie im Code zu verarbeiten und meine Schnittstelle zur Datenbank sauber zu halten. Ich würde also wetten, dass 75% meines C # -Codes im Gegensatz zu int Byte oder Short verwenden, da dies in der Datenbank enthalten ist.

Möglichkeiten: Bedeutet dies, dass die meisten Benutzer, die nur int für alles im Code verwenden, auch den Datentyp int für ihre SQL-Speicherdatentypen verwenden und sich möglicherweise weniger um die Gesamtgröße ihrer Datenbank kümmern, oder ob sie system.convertieren, wo immer dies möglich ist?

Warum es mich interessiert: Ich habe für immer alleine gearbeitet und möchte nur mit Best Practices und Standardcodierungskonventionen vertraut sein.

Brottruck
quelle
Die ursprüngliche Frage hinterließ den Eindruck, dass ich gefragt habe, ob es einen Grund gibt, warum ich Byte oder Smallint zugunsten von int vermeiden sollte. Ich möchte wirklich wissen, warum es besser ist, int überall zu verwenden, als Byte oder Smallint, wenn diese Datentypen ausreichen würden.
Breadtruck
Wenn Sie also zustimmen, int überall zu verwenden, möchte ich wissen, was sozusagen der Vorteil ist, bessere Leistung, keine Konvertierungen, warum sollte ich int überall verwenden
Breadtruck

Antworten:

96

In Bezug auf die Leistung ist ein Int in fast allen Fällen schneller. Die CPU arbeitet effizient mit 32-Bit-Werten.

Kürzere Werte sind kompliziert zu handhaben. Um beispielsweise ein einzelnes Byte zu lesen, muss die CPU den 32-Bit-Block lesen, der es enthält, und dann die oberen 24 Bits ausblenden.

Um ein Byte zu schreiben, muss es den 32-Bit-Zielblock lesen, die unteren 8 Bits mit dem gewünschten Bytewert überschreiben und den gesamten 32-Bit-Block erneut zurückschreiben.

In Bezug auf den Speicherplatz sparen Sie natürlich einige Bytes, indem Sie kleinere Datentypen verwenden. Wenn Sie also eine Tabelle mit einigen Millionen Zeilen erstellen, sollten kürzere Datentypen in Betracht gezogen werden. (Und das gleiche könnte ein guter Grund sein, warum Sie kleinere Datentypen in Ihrer Datenbank verwenden sollten)

Und was die Korrektheit betrifft, läuft ein Int nicht leicht über. Was ist, wenn Sie glauben, dass Ihr Wert in ein Byte passt und dann irgendwann in der Zukunft eine harmlos aussehende Änderung des Codes dazu führt, dass größere Werte darin gespeichert werden?

Dies sind einige der Gründe, warum int Ihr Standarddatentyp für alle integralen Daten sein sollte. Verwenden Sie Byte nur, wenn Sie tatsächlich Maschinenbytes speichern möchten. Verwenden Sie Shorts nur, wenn Sie mit einem Dateiformat oder Protokoll oder ähnlichem arbeiten, das tatsächlich 16-Bit-Ganzzahlwerte angibt. Wenn Sie im Allgemeinen nur mit ganzen Zahlen arbeiten, machen Sie sie zu Ints.

jalf
quelle
3
sollte ich longstandardmäßig auf 64-Bit-Computern verwenden? (Wenn Sie interessiert sind, werfen Sie bitte einen Blick auf diese Frage )
Alexander Malakhov
2
@ Alexander: Wahrscheinlich nicht. Zumindest auf x86 ist 32-Bit immer noch die beste "Standard" -Wahl (64-Bit-Anweisungen sind länger und Sie erhalten mehr Speicherverkehr mit größeren Werten). Wenn Sie .NET auf eine Architektur portieren, die keine dedizierten 32-Bit-Anweisungen enthält oder in der sie erheblich langsamer sind, dann ... wer weiß. Benchmarking und sehen, was am effizientesten ist. ;)
Jalf
2
@ Alexander: Ja und nein. Es geht nicht nur darum, welche Größenregister verfügbar sind, sondern auch darum, wie effizient sie implementiert sind und wie sie mit der Speicherbusbreite und anderen Faktoren zusammenspielen. Eine moderne CPU geht davon aus, dass die meisten Ganzzahlen 32 Bit breit sind, und versucht, für diesen Fall zu optimieren. Wenn Sie unbedingt jeden letzten Taktzyklus der Leistung herausdrücken müssen , dann vergleichen Sie ihn. Es ist möglich, dass sich in bestimmten Fällen kleinere variable Größen lohnen.
Jalf
4
Fühlen Sie sich frei, longüberall zu verwenden . Es schadet nichts. Dies ist jedoch nicht konsequent möglich , da die meisten .NET- (oder Java-) APIs intfast überall verwendet werden. Sie müssen Ihren Code also wahrscheinlich mit mehr Casts verunreinigen, als Sie es sonst hätten.
Jalf
2
Ich möchte hinzufügen, dass "Die CPU ist so ausgelegt, dass sie effizient mit 32-Bit-Werten arbeitet" der Grund dafür ist, dass Integraltypen, die kürzer als int32 sind, in der Zwischensprache nicht einmal arithmetische Unterstützung haben (z. B. b ++, wobei b Byte ist, ist tatsächlich int tmp = b; tmp ++; b = (Byte) tmp;)
Aloraman
26

Ich bin nur 6 Jahre zu spät, aber vielleicht kann ich jemand anderem helfen.

Hier sind einige Richtlinien, die ich verwenden würde:

  • Wenn die Möglichkeit besteht, dass die Daten in Zukunft nicht mehr passen, verwenden Sie den größeren int-Typ.
  • Wenn die Variable als Struktur- / Klassenfeld verwendet wird, wird sie standardmäßig aufgefüllt, um ohnehin die gesamten 32-Bit zu belegen, sodass durch die Verwendung von Byte / Int16 kein Speicherplatz gespart wird.
  • Wenn die Variable nur von kurzer Dauer ist (wie in einer Funktion), helfen die kleineren Datentypen nicht viel.
  • "byte" oder "char" können die Daten manchmal besser beschreiben und die Kompilierungszeit überprüfen, um sicherzustellen, dass ihnen nicht versehentlich größere Werte zugewiesen werden. Wenn Sie beispielsweise den Tag des Monats (1-31) mit einem Byte speichern und versuchen, ihm 1000 zuzuweisen, wird ein Fehler verursacht.
  • Wenn die Variable in einem Array von ungefähr 100 oder mehr verwendet wird, würde ich den kleineren Datentyp verwenden, solange dies sinnvoll ist.
  • Byte- und Int16-Arrays sind nicht so threadsicher wie ein Int (ein Grundelement).

Ein Thema, das niemand angesprochen hat, ist der begrenzte CPU-Cache. Kleinere Programme werden schneller ausgeführt als größere, da die CPU mehr Programme in die schnelleren L1 / L2 / L3-Caches einpassen kann.

Die Verwendung des int-Typs kann zu weniger CPU-Anweisungen führen, erzwingt jedoch auch, dass ein höherer Prozentsatz des Datenspeichers nicht in den CPU-Cache passt. Anweisungen sind billig auszuführen. Moderne CPU-Kerne können 3-7 Befehle pro Taktzyklus ausführen, ein einzelner Cache-Fehler kann jedoch 1000-2000 Taktzyklen kosten, da er bis zum RAM reichen muss.

Wenn Speicherplatz gespart wird, führt dies auch dazu, dass der Rest der Anwendung eine bessere Leistung erbringt, da er nicht aus dem Cache herausgedrückt wird.

Ich habe einen schnellen Summentest mit Zugriff auf zufällige Daten in zufälliger Reihenfolge durchgeführt, wobei sowohl ein Byte-Array als auch ein Int-Array verwendet wurden.

const int SIZE = 10000000, LOOPS = 80000;
byte[] array = Enumerable.Repeat(0, SIZE).Select(i => (byte)r.Next(10)).ToArray();
int[] visitOrder = Enumerable.Repeat(0, LOOPS).Select(i => r.Next(SIZE)).ToArray();

System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
int sum = 0;
foreach (int v in visitOrder)
    sum += array[v];
sw.Stop();

Hier sind die Ergebnisse in der Zeit (Ticks): (x86, Release-Modus, ohne Debugger, .NET 4.5, I7-3930k) (kleiner ist besser)

________________ Array Size __________________
       10  100   1K   10K  100K    1M   10M 
byte: 549  559  552   552   568   632  3041  
int : 549  566  552   562   590  1803  4206
  • Der zufällige Zugriff auf 1 Million Elemente mit Byte auf meiner CPU führte zu einer Leistungssteigerung von 285%!
  • Alles unter 10.000 war kaum wahrnehmbar.
  • int war für diesen Basissummentest nie schneller als Byte.
  • Diese Werte variieren je nach CPU mit unterschiedlichen Cache-Größen.

Ein letzter Hinweis: Manchmal schaue ich mir das jetzt Open-Source-.NET-Framework an, um zu sehen, was die Experten von Microsoft tun. Das .NET Framework verwendet überraschend wenig Byte / Int16. Ich konnte eigentlich keine finden.

Sunsetquest
quelle
1
Könnten Sie bitte mehr zu "Byte- und Int16-Arrays sind nicht so threadsicher wie ein Int (ein Primitiv)" erklären?
Rahul Rastogi
2
Ich glaube, wenn ein 8- oder 16-Bit-Wert in einem Array in x86 aktualisiert wird, muss er ein vollständiges 32-Bit-Wort aus dem Speicher lesen, dann einen Teil davon aktualisieren (das Byte, das aktualisiert werden muss) und dann diesen vollständigen 32-Bit-Wert zurückschreiben . Wenn also ein Thread gleichzeitig am ersten Byte und ein anderer Thread am zweiten Byte arbeitet, schlägt einer von ihnen die anderen Änderungen aus. Dies liegt daran, dass sie gleichzeitig an demselben 32-Bit-Block oder Speicher arbeiten. Dieses Problem kann auch in einem kompakten Array auftreten. Außerhalb eines Arrays wird ein 8/16-Bit-Wert auf eine 32-Bit-Speichergröße aufgefüllt, sodass dies in Ordnung ist.
Sunsetquest
9

Sie müssten sich mit einigen MILLIARDEN Zeilen befassen, bevor dies einen signifikanten Unterschied in Bezug auf die Speicherkapazität bewirkt. Nehmen wir an, Sie haben drei Spalten, und anstatt einen byteäquivalenten Datenbanktyp zu verwenden, verwenden Sie einen int-äquivalenten.

Das ergibt 3 (Spalten) x 3 (zusätzliche Bytes) pro Zeile oder 9 Bytes pro Zeile.

Dies bedeutet, dass Sie für "einige Millionen Zeilen" (sagen wir drei Millionen) zusätzliche 27 Megabyte Speicherplatz verbrauchen! Zum Glück sollten Sie sich darüber keine Sorgen machen müssen, da wir nicht mehr in den 1970ern leben :)

Wie oben erwähnt, beenden Sie die Mikrooptimierung - der Leistungseinbruch beim Konvertieren in / von verschiedenen ganzzahligen numerischen Typen wird Sie viel, viel schwerer treffen als die Bandbreiten- / Speicherplatzkosten, es sei denn, Sie haben es mit sehr, sehr, sehr großen Kosten zu tun Datensätze.

Jon Grant
quelle
7

Zum größten Teil "Nein".

Wenn Sie nicht im Voraus wissen, dass Sie mit Hunderten von Millionen Zeilen zu tun haben, handelt es sich um eine Mikrooptimierung.

Tun Sie, was am besten zum Domain-Modell passt. Wenn Sie später Leistungsprobleme haben, können Sie anhand des Benchmarks und des Profils genau bestimmen, wo sie auftreten.

Mitch Wheat
quelle
3
Ich glaube, Sie sagen "Nein" zur Verwendung dieser Typen, obwohl die Frage, ob sie vermieden werden sollen, etwas mehrdeutig ist. Auf jeden Fall ist es ein guter Rat bezüglich der Mikrooptimierung.
Noldorin
1
Also schlagen Sie beide vor, auf der ganzen Linie bei int zu bleiben, es sei denn, es sind Millionen von Zeilen und Sie beschäftigen sich mit Mikrooptimierung?
Breadtruck
1
Ja, um sich an int zu halten, es sei denn, in der Domain ist ein winziger Hinweis (zum Beispiel) sinnvoller. Wenn ich Mikrooptimierung sage, meine ich, dass es eine schlechte Idee ist. Es ist nicht der Weg zur Optimierung.
Mitch Wheat
3
Ich denke, "es sei denn in der Domäne macht ein winziger Sinn mehr Sinn" ist irgendwie widersprüchlich. Die Frage, die ich stelle, ist, obwohl ich ein Byte oder Smallint verwenden könnte, basierend auf der Nummer, die ich speichere, scheint es, dass jeder ein Int bevorzugt oder verwendet, selbst auf der Datenbankseite. Die Verwendung von Ints überall in der Datenbank, nur weil sie für Programmierzwecke besser zugeordnet sind, erscheint mir albern, aber aufgrund meiner begrenzten .Net-Erfahrung scheint dies wahrscheinlich der einfachste Weg zu sein. Easy übersetzt nicht immer den „richtigen Weg“
Breadtruck
1
@Breadtruck: guter Punkt. Ich sage, es gibt keine große Sache mit der Verwendung von int überall, es sei denn, Sie werden mit einer großen Anzahl von Zeilen zu tun haben. Ich sage auch, wenn ein winziger Hinweis in der Domäne sinnvoller ist, würde ich ihn aufgrund der zusätzlichen impliziten Informationen, die er vermittelt, eher verwenden.
Mitch Wheat
5

Nicht dass ich Jon Grant und anderen nicht geglaubt hätte, aber ich musste mich mit unserer "Million Row Table" selbst davon überzeugen. Die Tabelle hat 1.018.000. Ich habe 11 tinyint-Spalten und 6 smallint-Spalten in int konvertiert, es gab bereits 5 int & 3 smalldatetimes. 4 verschiedene Indizes verwendeten eine Kombination der verschiedenen Datentypen, aber offensichtlich verwenden die neuen Indizes jetzt alle int-Spalten.

Das Vornehmen der Änderungen kostete mich nur 40 MB bei der Berechnung der Festplattennutzung der Basistabelle ohne Indizes. Als ich die Indizes wieder in die Gesamtänderung einfügte, betrug der Unterschied insgesamt nur 30 MB. Ich war also überrascht, weil ich dachte, die Indexgröße wäre größer.

30 MB sind es also wert, all die verschiedenen Datentypen zu verwenden, No Way! Ich mache mich auf den Weg ins INT-Land. Vielen Dank an alle, die diesen anal-zurückhaltenden Programmierer wieder in das geradlinige und glückliche Leben ohne weitere ganzzahlige Konvertierungen zurückversetzt haben ... yippeee!

Brottruck
quelle
3
Und was ist mit DB Cash? Dies ist ein wichtiger Faktor für die Gesamtleistung der Datenbank. Ich meine, wie viel Prozent sind 30 MB? Ich würde zweimal überlegen, bevor ich Bargeld effektiv um 30% reduziere
Alexander Malakhov
4

Wenn int überall verwendet wird, sind keine Castings oder Konvertierungen erforderlich. Das ist ein größerer Gewinn für den Geldbeutel als der Speicher, den Sie durch die Verwendung mehrerer ganzzahliger Größen sparen.

Es macht das Leben einfach einfacher.

Robert Harvey
quelle