Warum sollte uint32_t gegenüber uint_fast32_t bevorzugt werden?

81

Es scheint, dass dies uint32_tviel häufiger vorkommt als uint_fast32_t(mir ist klar, dass dies ein anekdotischer Beweis ist). Das erscheint mir allerdings kontraintuitiv.

Fast immer, wenn ich sehe, dass eine Implementierung verwendet wird uint32_t, ist alles, was sie wirklich will, eine Ganzzahl, die Werte bis zu 4.294.967.295 enthalten kann (normalerweise eine viel niedrigere Grenze irgendwo zwischen 65.535 und 4.294.967.295).

Es scheint seltsam zu sein, diese zu verwenden uint32_t, da die Garantie "genau 32 Bit" nicht benötigt wird und die Garantie "schnellste verfügbare> = 32 Bit" von uint_fast32_tgenau die richtige Idee zu sein scheint. Darüber hinaus ist, obwohl es normalerweise implementiert uint32_tist, nicht garantiert, dass es existiert.

Warum wäre uint32_tdann bevorzugt? Ist es einfach besser bekannt oder gibt es technische Vorteile gegenüber den anderen?

Joost
quelle
24
Einfache Antwort, vielleicht brauchen sie eine ganze Zahl, die genau 32 Bit hat?
Stargateur
7
Zuerst habe ich gehört uint32_fast_t, was, wenn ich es richtig verstehe, mindestens 32 Bit ist (was bedeutet, dass es mehr sein könnte? Klingt für mich irreführend). Ich verwende derzeit uint32_tund Freunde in meinem Projekt, weil ich diese Daten packe und über das Netzwerk sende. Ich möchte, dass Sender und Empfänger genau wissen, wie groß die Felder sind. Klingt so, als wäre dies möglicherweise nicht die robusteste Lösung, da eine Plattform möglicherweise nicht implementiert wird uint32_t, aber alle von mir tun dies anscheinend, sodass ich mit dem, was ich tue, einverstanden bin.
Yano
5
@yano: Für das Networking sollten Sie sich auch um die Bytereihenfolge / Endianess kümmern - uint32_tdas gibt Ihnen das nicht (und es ist schade, dass es kein uint32_t_beund gibt uint32_t_le, was für fast jeden möglichen Fall besser geeignet wäre, in dem uint32_tderzeit die beste Option ist).
Brendan
3
@Brendan - würden htonl () und ntohl () in Bezug auf _be und _le dieselbe Fähigkeit bieten?
mpez0
2
@Brendan, das ist ein ziemlich schweres Objekt, das in einem Standard-Int versteckt werden kann, bei dem es sich allesamt um primitive Typen handelt. Ich stimme Ihnen im Prinzip zu, dass dies irgendwo im Standard behandelt werden sollte, aber ich denke, dass dies möglicherweise nicht der
Steve Cox

Antworten:

78

uint32_twird garantiert fast die gleichen Eigenschaften auf jeder Plattform haben, die es unterstützt. 1

uint_fast32_t hat nur sehr wenige Garantien dafür, wie es sich auf verschiedenen Systemen im Vergleich verhält.

Wenn Sie zu einer Plattform uint_fast32_tmit einer anderen Größe wechseln , muss der gesamte verwendete Code uint_fast32_terneut getestet und validiert werden. Alle Stabilitätsannahmen werden aus dem Fenster sein. Das gesamte System wird anders funktionieren.

Wenn Sie Ihren Code schreiben, haben Sie möglicherweise nicht einmal Zugriff auf ein uint_fast32_tSystem mit einer Größe von 32 Bit.

uint32_t funktioniert nicht anders (siehe Fußnote).

Korrektheit ist wichtiger als Geschwindigkeit. Vorzeitige Korrektheit ist daher ein besserer Plan als vorzeitige Optimierung.

Für den Fall, dass ich Code für Systeme uint_fast32_tmit 64 oder mehr Bits schreibe , kann ich meinen Code für beide Fälle testen und verwenden. Abgesehen von Bedarf und Gelegenheit ist dies ein schlechter Plan.

Schließlich , uint_fast32_twenn Sie es für längere Zeit oder Anzahl der Instanzen kann langsamer als sein werden speichert uint32einfach Größe aufgrund von Cache - Probleme und Speicherbandbreite. Heutzutage sind Computer weitaus häufiger speichergebunden als CPU-gebunden und uint_fast32_tkönnen isoliert schneller sein, jedoch nicht, nachdem Sie den Speicheraufwand berücksichtigt haben.


1 Wie @chux in einem Kommentar festgestellt hat, durchläuft die Arithmetik on , wenn sie unsignedgrößer als ist uint32_t, uint32_tdie üblichen ganzzahligen Beförderungen, und wenn nicht, bleibt sie unverändert uint32_t. Dies kann zu Fehlern führen. Nichts ist jemals perfekt.

Yakk - Adam Nevraumont
quelle
15
"uint32_t hat garantiert auf jeder Plattform, die dies unterstützt, dieselben Eigenschaften." Es gibt ein Eckproblem, wenn unsignedes breiter als ist uint32_tund dann uint32_tauf einer Plattform die üblichen ganzzahligen Aktionen durchläuft und auf einer anderen nicht. Mit uint32_tdiesen ganzzahligen mathematischen Problemen werden sie jedoch erheblich reduziert.
chux
2
@chux Ein Eckfall, der beim Multiplizieren zu UB führen kann, da die Heraufstufung einen vorzeichenbehafteten int und einen vorzeichenbehafteten ganzzahligen Überlauf bevorzugt, ist UB.
CodesInChaos
2
Obwohl diese Antwort soweit richtig ist, werden die wichtigsten Details sehr heruntergespielt. Kurz gesagt, uint32_tist dort, wo die genauen Details der Maschinendarstellung des Typs wichtig sind, während uint_fast32_tdort, wo die Rechengeschwindigkeit am wichtigsten ist, (Un-) Vorzeichen und Mindestreichweite wichtig sind und Details der Darstellung nicht wesentlich sind. Es gibt auch uint_least32_tFälle, in denen (Un-) Signiertheit und Mindestreichweite am wichtigsten sind, Kompaktheit wichtiger als Geschwindigkeit ist und eine genaue Darstellung nicht wesentlich ist.
John Bollinger
@ JohnBollinger Das ist alles schön und gut, aber ohne Tests auf tatsächlicher Hardware, die mehr als eine Variante implementiert, sind die Typen mit variabler Größe eine Falle. Und der Grund, warum Leute uint32_teher als die anderen Typen verwenden, ist, dass sie normalerweise keine solche Hardware haben, auf der sie testen können . (Gleiches gilt int32_tin geringerem Maße und sogar intund short).
Yakk - Adam Nevraumont
1
Ein Beispiel für den Eckfall: Let unsigned short== uint32_tund int== int48_t. Wenn Sie so etwas wie berechnen (uint32_t)0xFFFFFFFF * (uint32_t)0xFFFFFFFF, werden die Operanden hochgestuft signed intund lösen einen vorzeichenbehafteten Ganzzahlüberlauf aus, was ein undefiniertes Verhalten ist. Siehe diese Frage.
Nayuki
32

Warum verwenden viele Menschen uint32_teher als uint32_fast_t?

Hinweis: Falsch benannt uint32_fast_tsollte sein uint_fast32_t.

uint32_that eine strengere Spezifikation als uint_fast32_tund sorgt so für eine konsistentere Funktionalität.


uint32_t Profis:

  • Verschiedene Algorithmen spezifizieren diesen Typ. IMO - bester Grund zu verwenden.
  • Genaue Breite und Reichweite bekannt.
  • Arrays dieser Art fallen nicht an.
  • Ganzzahlige Mathematik ohne Vorzeichen mit Überlauf ist vorhersehbarer.
  • Engere Übereinstimmung in Reichweite und Mathematik der 32-Bit-Typen anderer Sprachen.
  • Nie gepolstert.

uint32_t Nachteile:

  • Nicht immer verfügbar (dies ist jedoch im Jahr 2018 selten).
    Beispiel: Plattformen ohne 8/16/32-Bit-Ganzzahlen ( 9/18 / 36- Bit, andere ).
    Beispiel: Plattformen mit Nicht-2-Komplement. alte 2200

uint_fast32_t Profis:

  • Immer verfügbar.
    Dies ermöglicht immer allen neuen und alten Plattformen, schnelle / minimale Typen zu verwenden.
  • "Schnellster" Typ, der 32-Bit-Bereich unterstützt.

uint_fast32_t Nachteile:

  • Reichweite ist nur minimal bekannt. Beispiel: Es könnte sich um einen 64-Bit-Typ handeln.
  • Arrays dieses Typs können im Speicher verschwenderisch sein.
  • Alle Antworten (meine zuerst auch), der Beitrag und die Kommentare verwendeten den falschen Namen uint32_fast_t. Sieht so aus, als ob viele diesen Typ einfach nicht brauchen und benutzen. Wir haben nicht einmal den richtigen Namen verwendet!
  • Polsterung möglich - (selten).
  • In ausgewählten Fällen kann der "schnellste" Typ wirklich ein anderer Typ sein. Es uint_fast32_thandelt sich also nur um eine Annäherung 1. Ordnung.

Am Ende hängt das Beste vom Codierungsziel ab. Verwenden Sie diese Option, es sei denn, Sie codieren für eine sehr breite Portabilität oder eine Nischenleistungsfunktion uint32_t.


Bei der Verwendung dieser Typen kommt ein weiteres Problem ins Spiel: ihr Rang im Vergleich zu int/unsigned

Vermutlich uint_fastN_tkönnte der Rang von sein unsigned. Dies ist nicht spezifiziert, sondern eine bestimmte und überprüfbare Bedingung.

Somit uintN_tist es wahrscheinlicher als uint_fastN_tenger zu sein unsigned. Dies bedeutet, dass Code, der uintN_tMathematik verwendet, mit größerer Wahrscheinlichkeit ganzzahligen Beförderungen unterliegt als uint_fastN_tin Bezug auf die Portabilität.

Mit diesem Problem: Portabilitätsvorteil uint_fastN_tbei ausgewählten mathematischen Operationen.


Randnotiz über int32_tanstatt int_fast32_t: Auf seltenen Maschinen INT_FAST32_MINkann -2.147.483.647 und nicht -2.147.483.648 sein. Der größere Punkt: (u)intN_tTypen sind eng spezifiziert und führen zu portablem Code.

chux - Monica wieder einsetzen
quelle
2
Schnellster Typ, der 32-Bit-Bereich unterstützt => wirklich? Dies ist ein Relikt einer Zeit, in der RAM mit CPU-Geschwindigkeit lief. Heutzutage hat sich das Gleichgewicht auf PCs dramatisch verschoben, sodass (1) das Abrufen von 32-Bit-Ganzzahlen aus dem Speicher doppelt so schnell ist wie das Abrufen von 64-Bit-Ganzzahlen und (2) vektorisierten Anweisungen Bei 32-Bit-Ganzzahlen knirschen doppelt so viele wie bei 64-Bit-Ganzzahlen. Ist es immer noch das schnellste?
Matthieu M.
4
Für einige Dinge am schnellsten, für andere langsamer. Es gibt keine einheitliche Antwort auf die Frage, was die schnellste Ganzzahlgröße ist, wenn Sie Arrays in Betracht ziehen oder keine Erweiterung benötigen. In x86-64 System V ABI uint32_fast_thandelt es sich um einen 64-Bit-Typ, der die gelegentliche Vorzeichenerweiterung speichert und imul rax, [mem]bei Verwendung mit 64-Bit-Ganzzahlen oder -Zeigern anstelle eines separaten Ladebefehls mit null Erweiterung ermöglicht . Aber das ist alles, was Sie für den Preis des doppelten Cache-Footprints und der zusätzlichen Codegröße (REX-Präfix auf allem) erhalten
Peter Cordes
1
Außerdem ist die 64-Bit-Division auf den meisten x86-CPUs viel langsamer als die 32-Bit-Division, und einige (wie die Bulldozer-Familie, Atom und Silvermont) haben eine langsamere 64-Bit-Multiplikation als die 32. Die Bulldozer-Familie hat auch eine langsamere 64-Bit-Division popcnt. Und denken Sie daran, dass es nur sicher ist, diesen Typ für 32-Bit-Werte zu verwenden, da er auf anderen Architekturen kleiner ist, sodass Sie diese Kosten für nichts bezahlen.
Peter Cordes
2
Ich würde erwarten, dass es als gewichteter Durchschnitt über alle C- und C ++ - Anwendungen uint32_fast_teine schreckliche Wahl ist, auf x86 zu arbeiten. Die Operationen, die schneller sind, sind imul rax, [mem]selten und der Vorteil, wenn sie auftreten, ist meist winzig : Die Unterschiede für den Fall, dass @PeterCordes erwähnt, sind sehr , sehr gering: ein einzelnes UOP in der fusionierten Domäne und Null in der nicht fusionierten Domäne. In den interessantesten Szenarien wird nicht einmal ein einziger Zyklus hinzugefügt. Wägen Sie ab, dass es gegen die doppelte Speichernutzung und eine schlechtere Vektorisierung schwer fällt, sehr oft zu gewinnen.
BeeOnRope
2
@ PeterCordes - interessant aber auch schrecklich :). Es würde fast_tnoch schlimmer machen int: Es hat nicht nur unterschiedliche Größen auf unterschiedlichen Plattformen, sondern auch unterschiedliche Größen, abhängig von Optimierungsentscheidungen und unterschiedlichen Größen in unterschiedlichen Dateien! In der Praxis denke ich, dass es selbst bei der Optimierung des gesamten Programms nicht funktionieren kann: Die Größen in C und C ++ sind so festgelegt, dass sizeof(uint32_fast_t)alles, was bestimmt, dass es sogar direkt immer den gleichen Wert zurückgeben muss, für den Compiler sehr schwierig wäre mache eine solche Transformation.
BeeOnRope
25

Warum verwenden viele Menschen uint32_teher als uint32_fast_t?

Dumme Antwort:

  • Es gibt keinen Standardtyp uint32_fast_t, die korrekte Schreibweise ist uint_fast32_t.

Praktische Antwort:

  • Viele Leute verwenden tatsächlich uint32_toder int32_tfür ihre genaue Semantik genau 32 Bit mit vorzeichenlosem Wrap-Around-Arithmetik ( uint32_t) oder 2er-Komplementdarstellung ( int32_t). Die xxx_fast32_tTypen können größer und daher ungeeignet sein, um sie in Binärdateien zu speichern, in gepackten Arrays und Strukturen zu verwenden oder über ein Netzwerk zu senden. Darüber hinaus sind sie möglicherweise nicht einmal schneller.

Pragmatische Antwort:

  • Viele Leute wissen es einfach nicht (oder kümmern sich einfach nicht darum) uint_fast32_t, wie in Kommentaren und Antworten gezeigt, und gehen wahrscheinlich davon aus unsigned int, dass sie dieselbe Semantik haben, obwohl viele aktuelle Architekturen immer noch 16-Bit- intWerte haben und einige seltene Museumsbeispiele andere seltsame int-Größen unter 32.

UX-Antwort:

  • Obwohl möglicherweise schneller als uint32_t, uint_fast32_tist die Verwendung langsamer: Die Eingabe dauert länger, insbesondere wenn Rechtschreibung und Semantik in der C-Dokumentation nachgeschlagen werden ;-)

Eleganz ist wichtig (offensichtlich meinungsbasiert):

  • uint32_tsieht so schlecht aus, dass viele Programmierer es vorziehen, ihre eigenen u32oder uint32Typen zu definieren ... Aus dieser Perspektive uint_fast32_tsieht es irreparabel aus. Kein Wunder, dass es mit seinen Freunden uint_least32_tund so auf der Bank sitzt .
chqrlie
quelle
+1 für UX. Es ist besser als std::reference_wrapperich denke, aber manchmal frage ich mich, ob das Standardkomitee wirklich will, dass die Typen, die es standardisiert, verwendet werden ...
Matthieu M.
7

Ein Grund dafür ist, dass unsigned intes bereits "am schnellsten" ist, ohne dass spezielle Typedefs erforderlich sind oder etwas hinzugefügt werden muss. Wenn Sie es also schnell brauchen, verwenden Sie einfach das Fundamental intoder den unsigned intTyp.
Der Standard garantiert zwar nicht ausdrücklich, dass er am schnellsten ist, tut dies jedoch indirekt , indem er in 3.9.1 angibt, dass "einfache Ints die natürliche Größe haben, die von der Architektur der Ausführungsumgebung vorgeschlagen wird" . Mit anderen Worten, int(oder sein nicht signiertes Gegenstück) ist das, womit sich der Prozessor am wohlsten fühlt.

Jetzt wissen Sie natürlich nicht, wie groß das sein unsigned intkönnte. Sie wissen nur, dass es mindestens so groß ist wie short(und ich scheine mich zu erinnern, dass shortes mindestens 16 Bit sein muss, obwohl ich das jetzt im Standard nicht finden kann!). Normalerweise sind es einfach nur 4 Bytes, aber theoretisch könnte es größer oder im Extremfall sogar kleiner sein ( obwohl ich persönlich noch nie auf eine Architektur gestoßen bin, in der dies der Fall war, nicht einmal auf 8-Bit-Computern in den 1980er Jahren. .. vielleicht waren einige Mikrocontroller, die wissen , dass ich an Demenz leide, intdamals ganz klar 16 Bit).

Der C ++ - Standard gibt nicht an, welche <cstdint>Typen es gibt oder was sie garantieren, sondern erwähnt lediglich "wie in C".

uint32_tgarantiert gemäß dem C-Standard, dass Sie genau 32 Bit erhalten. Nichts anderes, nichts weniger und keine Füllbits. Manchmal ist dies genau das, was Sie brauchen, und daher ist es sehr wertvoll.

uint_least32_tgarantiert, dass es unabhängig von der Größe nicht kleiner als 32 Bit sein kann (aber es könnte sehr gut größer sein). Manchmal, aber viel seltener als ein genaues Witz oder "egal", ist dies das, was Sie wollen.

Schließlich uint_fast32_tist meiner Meinung nach etwas überflüssig, außer zu Zwecken der Absichtserklärung. Der C-Standard besagt "bezeichnet einen ganzzahligen Typ, der normalerweise am schnellsten ist" (beachten Sie das Wort "normalerweise") und erwähnt ausdrücklich, dass er nicht für alle Zwecke am schnellsten sein muss. Mit anderen Worten, uint_fast32_tist ungefähr das Gleiche wie uint_least32_t, was normalerweise auch am schnellsten ist, nur keine Garantie gegeben (aber keine Garantie so oder so).

Da Sie sich die meiste Zeit entweder nicht um die genaue Größe kümmern oder genau 32 (oder 64, manchmal 16) Bits möchten und der unsigned intTyp "egal" sowieso am schnellsten ist, erklärt dies, warum dies nicht der Fall uint_fast32_tist häufig benutzt.

Damon
quelle
3
Ich bin überrascht, dass Sie sich intauf 8-Bit-Prozessoren nicht an 16-Bit erinnern. Ich kann mich an keine aus jenen Tagen erinnern, in denen etwas Größeres verwendet wurde. Wenn Speicher zur Verfügung steht, verwendeten Compiler für segmentierte x86-Architektur ebenfalls 16-Bit int.
Mark Ransom
@ MarkRansom: Wow, du hast recht. Ich war sooooo überzeugt, dass das 32000 int32 Bit waren (was ich mir als Beispiel vorgestellt habe). Es war nicht ...
Damon
intsollte in der Vergangenheit der schnellste Typ mit einer minimalen Breite von 16 Bit sein (aus diesem Grund hat C eine ganzzahlige Heraufstufungsregel), aber heute ist dies bei 64-Bit-Architekturen nicht mehr der Fall. Zum Beispiel sind 8-Byte-Ganzzahlen schneller als 4-Byte-Ganzzahlen auf x86_64-Bit, da der Compiler bei 4-Byte-Ganzzahlen einen zusätzlichen Befehl einfügen muss, der den 4-Byte-Wert in einen 8-Byte-Wert erweitert, bevor er ihn mit anderen 8-Byte-Werten vergleicht.
StaceyGirl
"unsigned int" ist auf x64 nicht unbedingt am schnellsten. Seltsame Dinge sind passiert.
Joshua
Ein weiterer häufiger Fall ist, dass longaus historischen Gründen 32-Bit sein muss und intjetzt nicht breiter als sein muss long, sodass int32-Bit möglicherweise beibehalten werden muss, selbst wenn 64 Bit schneller wären.
Davislor
6

Ich habe keine Beweise gesehen, uint32_tdie für seine Reichweite verwendet werden könnten. Stattdessen wird die meiste Zeit, die ich gesehen habeuint32_t , genau 4 Oktette Daten in verschiedenen Algorithmen gespeichert, mit garantierter Umlauf- und Verschiebungssemantik!

Es gibt auch andere Gründe für die Verwendung uint32_tanstelle von uint_fast32_t: Oft ist es so, dass es einen stabilen ABI liefert. Zusätzlich kann die Speichernutzung genau bekannt sein. Dies gleicht sehr viel aus, unabhängig davon, von welchem ​​Geschwindigkeitsgewinn er stammen würde uint_fast32_t, wenn sich dieser Typ von dem von unterscheidet uint32_t.

Für Werte <65536 gibt es bereits einen praktischen Typ, der aufgerufen wird unsigned int( unsigned shortmuss mindestens diesen Bereich haben, hat aber unsigned intdie native Wortgröße). Für Werte <4294967296 gibt es einen anderen aufgerufenen Typ unsigned long.


Und schließlich verwenden die Leute nicht, uint_fast32_tweil es ärgerlich lang zu tippen und leicht zu tippen ist: D.

Antti Haapala
quelle
@ikegami: Du hast meine Absicht mit der shortBearbeitung geändert . intist vermutlich das schnelle , wenn es sich von unterscheidet short.
Antti Haapala
1
Ihr letzter Satz ist also völlig falsch. Wenn Sie behaupten, dass Sie unsigned intanstelle von verwenden sollten uint16_fast_t, behaupten Sie, Sie wissen es besser als der Compiler.
Ikegami
Ich entschuldige mich auch dafür, dass ich die Absicht Ihres Textes geändert habe. Das war nicht meine Absicht.
Ikegami
unsigned longist keine gute Wahl, wenn Ihre Plattform 64-Bit- longs hat und Sie nur Zahlen benötigen <2^32.
Ruslan
1
@ikegami: Der Typ "unsigned int" verhält sich immer als vorzeichenloser Typ, auch wenn er heraufgestuft wird. In dieser Hinsicht ist es sowohl uint16_tals auch überlegen uint_fast16_t. Wenn uint_fast16_tsie lockerer als normale Ganzzahltypen angegeben würden, sodass ihr Bereich für Objekte, deren Adressen nicht verwendet werden, nicht konsistent sein muss, könnte dies einige Leistungsvorteile auf Plattformen bieten, die intern 32-Bit-Arithmetik ausführen, aber über einen 16-Bit-Datenbus verfügen . Der Standard erlaubt jedoch keine solche Flexibilität.
Supercat
5

Mehrere Gründe.

  1. Viele Leute wissen nicht, dass es "schnelle" Typen gibt.
  2. Es ist ausführlicher zu tippen.
  3. Es ist schwieriger, über das Verhalten Ihres Programms nachzudenken, wenn Sie die tatsächliche Größe des Typs nicht kennen.
  4. Der Standard legt nicht wirklich am schnellsten fest, und es kann auch nicht wirklich kontextabhängig sein, welcher Typ tatsächlich am schnellsten ist.
  5. Ich habe keine Hinweise darauf gesehen, dass Plattformentwickler bei der Definition ihrer Plattformen über die Größe dieser Typen nachgedacht haben. Unter x86-64 Linux sind die "schnellen" Typen alle 64-Bit, obwohl x86-64 Hardware-Unterstützung für schnelle Operationen mit 32-Bit-Werten bietet.

Zusammenfassend sind die "schnellen" Typen wertloser Müll. Wenn Sie wirklich herausfinden müssen, welcher Typ für eine bestimmte Anwendung am schnellsten ist, müssen Sie Ihren Code auf Ihrem Compiler vergleichen.

Plugwash
quelle
In der Vergangenheit gab es Prozessoren mit 32-Bit- und / oder 64-Bit-Speicherzugriffsanweisungen, jedoch nicht mit 8- und 16-Bit. Int_fast {8,16} _t wäre also vor mehr als 20 Jahren nicht ganz dumm gewesen. AFAIK, der letzte derartige Mainstream-Prozessor, war der ursprüngliche DEC Alpha 21064 (die zweite Generation 21164 wurde verbessert). Wahrscheinlich gibt es immer noch eingebettete DSPs oder was auch immer, die nur Wortzugriffe ausführen , aber Portabilität ist bei solchen Dingen normalerweise kein großes Problem , daher verstehe ich nicht, warum Sie bei diesen schnell einen Frachtkult betreiben würden. Und es gab handgefertigte Cray-Maschinen "Alles ist 64-Bit".
user1998586
1
Kategorie 1b: Vielen Menschen ist es egal, dass es die "schnellen" Typen gibt. Das ist meine Kategorie.
Gnasher729
Kategorie 6: Viele Menschen vertrauen nicht darauf, dass die "schnellen" Typen die schnellsten sind. Ich gehöre zu dieser Kategorie.
Klarer
5

Unter dem Gesichtspunkt der Korrektheit und der einfachen Codierung uint32_that dies viele Vorteile, uint_fast32_tinsbesondere aufgrund der genau definierten Größe und arithmetischen Semantik, wie viele Benutzer oben hervorgehoben haben.

Was vielleicht wurde verpasst ist , dass der eine vermeintliche Vorteil von uint_fast32_t- , dass es sein kann , schneller , einfach nie in sinnvoller Weise verwirklicht. Die meisten 64-Bit-Prozessoren, die die 64-Bit-Ära dominiert haben (x86-64 und Aarch64 meistens), sind aus 32-Bit-Architekturen hervorgegangen und verfügen selbst im 64-Bit-Modus über schnelle native 32-Bit-Operationen. So uint_fast32_tist es auch uint32_tauf diesen Plattformen.

Selbst wenn einige der "ebenfalls ausgeführten" Plattformen wie POWER, MIPS64 und SPARC nur 64-Bit-ALU-Operationen anbieten, kann die überwiegende Mehrheit der interessanten 32-Bit-Operationen problemlos auf 64-Bit-Registern ausgeführt werden: die unteren 32-Bit-Operationen die gewünschten Ergebnisse erzielen (und auf allen gängigen Plattformen können Sie mindestens 32-Bit laden / speichern). Die Linksverschiebung ist das Hauptproblem, aber selbst das kann in vielen Fällen durch Optimierungen der Wert- / Bereichsverfolgung im Compiler optimiert werden.

Ich bezweifle, dass die gelegentlich etwas langsamere Linksverschiebung oder die 32x32 -> 64-Multiplikation die doppelte Speichernutzung für solche Werte in allen außer den dunkelsten Anwendungen überwiegen wird.

Abschließend möchte ich darauf hinweisen, dass der Kompromiss zwar weitgehend als "Speichernutzung und Vektorisierungspotential" (zugunsten uint32_t) gegenüber der Befehlsanzahl / Geschwindigkeit (zugunsten uint_fast32_t) charakterisiert wurde - auch das ist mir nicht klar. Ja, auf einigen Plattformen benötigen Sie zusätzliche Anweisungen für einige 32-Bit-Vorgänge, aber Sie speichern auch einige Anweisungen, weil:

  • Die Verwendung eines kleineren Typs ermöglicht es dem Compiler häufig, benachbarte Operationen geschickt zu kombinieren, indem eine 64-Bit-Operation verwendet wird, um zwei 32-Bit-Operationen auszuführen. Ein Beispiel für diese Art der "Vektorisierung des armen Mannes" ist nicht ungewöhnlich. Zum Beispiel kann das Erstellen einer Konstante struct two32{ uint32_t a, b; }in raxlike zu einer einzigen two32{1, 2} optimiert werden, mov rax, 0x20001während die 64-Bit-Version zwei Anweisungen benötigt. Im Prinzip sollte dies auch für benachbarte arithmetische Operationen (gleiche Operation, unterschiedlicher Operand) möglich sein, aber ich habe es in der Praxis nicht gesehen.
  • Eine geringere "Speichernutzung" führt häufig auch zu weniger Anweisungen, selbst wenn der Speicher- oder Cache-Footprint kein Problem darstellt, da alle Typstrukturen oder Arrays dieses Typs kopiert werden, erhalten Sie pro kopiertem Register das Doppelte für Ihr Geld.
  • Kleinere Datentypen nutzen häufig bessere moderne Anrufkonventionen wie das SysV ABI, mit denen Datenstrukturdaten effizient in Register gepackt werden. Beispielsweise können Sie in Registern bis zu einer 16-Byte-Struktur zurückgeben rdx:rax. Für eine Funktion, die eine Struktur mit 4 uint32_tWerten zurückgibt (initialisiert von einer Konstanten), wird dies übersetzt in

    ret_constant32():
        movabs  rax, 8589934593
        movabs  rdx, 17179869187
        ret
    

    Dieselbe Struktur mit 4 64-Bit uint_fast32_tbenötigt eine Registerverschiebung und vier Speicher im Speicher, um dasselbe zu tun (und der Aufrufer muss wahrscheinlich die Werte nach der Rückkehr aus dem Speicher zurücklesen):

    ret_constant64():
        mov     rax, rdi
        mov     QWORD PTR [rdi], 1
        mov     QWORD PTR [rdi+8], 2
        mov     QWORD PTR [rdi+16], 3
        mov     QWORD PTR [rdi+24], 4
        ret
    

    In ähnlicher Weise werden beim Übergeben von Strukturargumenten 32-Bit-Werte etwa doppelt so dicht in die für Parameter verfügbaren Register gepackt, sodass es weniger wahrscheinlich ist, dass Ihnen die Registerargumente ausgehen und Sie auf den Stapel 1 gelangen müssen .

  • Selbst wenn Sie sich uint_fast32_tfür Orte entscheiden, an denen "Geschwindigkeit wichtig ist", haben Sie häufig auch Orte, an denen Sie einen Typ mit fester Größe benötigen. Zum Beispiel, wenn Sie Werte für externe Ausgaben von externen Eingaben als Teil Ihres ABI, als Teil einer Struktur, die ein bestimmtes Layout benötigt, oder weil Sie intelligent uint32_tfür große Aggregationen von Werten verwenden, um Speicherplatz zu sparen. An den Orten , wo Ihr uint_fast32_tund `` uint32_t` Typen Schnittstelle benötigen, können Sie (zusätzlich zu der Entwicklung Komplexität), unnötige Zeichen Erweiterungen oder andere Größe-Mismatch - bezogenen Code finden. Compiler leisten in vielen Fällen gute Arbeit, um dies zu optimieren, aber es ist immer noch nicht ungewöhnlich, dass dies beim Mischen von Typen unterschiedlicher Größe in einer optimierten Ausgabe angezeigt wird.

Sie können mit einigen der obigen Beispiele und mehr auf Godbolt spielen .


1 Um klar zu sein, ist die Konvention, Strukturen dicht in Register zu packen, nicht immer ein klarer Gewinn für kleinere Werte. Dies bedeutet jedoch, dass die kleineren Werte möglicherweise "extrahiert" werden müssen, bevor sie verwendet werden können. Zum Beispiel benötigt eine einfache Funktion, die die Summe der beiden Strukturelemente zusammen zurückgibt, eine mov rax, rdi; shr rax, 32; add edi, eaxWeile. Für die 64-Bit-Version erhält jedes Argument ein eigenes Register und benötigt nur ein einzelnes addoder lea. Wenn Sie jedoch akzeptieren, dass das Design "Strukturen beim Übergeben dicht packen" insgesamt sinnvoll ist, werden kleinere Werte diese Funktion besser nutzen.

BeeOnRope
quelle
glibc unter x86-64 Linux verwendet 64-Bit uint_fast32_t, was IMO ein Fehler ist. (Anscheinend ist Windows unter Windows uint_fast32_tein 32-Bit-Typ.) 64-Bit unter x86-64 Linux zu sein, ist der Grund, warum ich niemandem empfehlen würde, es zu verwenden uint_fast32_t: Es ist für eine niedrige Befehlsanzahl optimiert (Funktionsargumente und Rückgabewerte benötigen für nie eine Erweiterung von Null Verwendung als Array-Index) nicht für die Gesamtgeschwindigkeit oder Codegröße auf einer der wichtigsten wichtigen Plattformen.
Peter Cordes
2
Oh, richtig, ich habe Ihren obigen Kommentar zum SysV ABI gelesen, aber wie Sie später darauf hinweisen, hat es vielleicht eine andere Gruppe / ein anderes Dokument entschieden - aber ich denke, wenn das passiert, ist es ziemlich in Stein gemeißelt. Ich denke, es ist sogar fraglich, ob die reine Zyklusanzahl / Befehlsanzahl größere Typen bevorzugt, selbst wenn Speicher-Footprint-Effekte und Vektorisierung ignoriert werden, selbst auf Plattformen ohne gute 32-Bit-Betriebsunterstützung - da es immer noch Fälle gibt, in denen der Compiler die kleineren Typen besser optimieren kann. Ich habe oben einige Beispiele hinzugefügt. @ PeterCordes
BeeOnRope
Das Packen mehrerer Strukturelemente in dasselbe Register kostet SysV ziemlich oft mehr Anweisungen, wenn ein pair<int,bool>oder zurückgegeben wird pair<int,int>. Wenn beide Mitglieder keine Konstanten zur Kompilierungszeit sind, gibt es normalerweise mehr als nur ein ODER, und der Aufrufer muss die Rückgabewerte entpacken. ( bugs.llvm.org/show_bug.cgi?id=34840 LLVM optimiert die Rückgabe von Rückgabewerten für private Funktionen und sollte 32-Bit-Int als Ganzes behandeln, raxsodass das boolgetrennt ist, dlanstatt eine 64-Bit-Konstante zu benötigen testit.)
Peter Cordes
1
Ich denke, Compiler teilen Funktionen im Allgemeinen nicht auf. Das Herausschälen eines Schnellpfads als separate Funktion ist eine nützliche Optimierung auf Quellenebene (insbesondere in einem Header, in dem er inline sein kann). Kann sehr gut sein, wenn 90% der Eingaben der Fall "nichts tun" sind; Das Filtern in der Anruferschleife ist ein großer Gewinn. IIRC, Linux verwendet __attribute__((noinline))genau, um sicherzustellen, dass gcc die Fehlerbehandlungsfunktion nicht einbindet, und stellt eine Reihe von push rbx/ .../ pop rbx/ ... auf den schnellen Weg einiger wichtiger Kernelfunktionen, die viele Aufrufer haben und selbst nicht inline sind.
Peter Cordes
1
In Java ist dies ebenfalls sehr wichtig, da Inlining für weitere Optimierungen von entscheidender Bedeutung ist (insbesondere für die De-Virtualisierung, die im Gegensatz zu C ++ allgegenwärtig ist). Daher lohnt es sich oft, dort einen schnellen Pfad aufzuteilen, und "Bytecode-Optimierung" ist tatsächlich eine Sache (trotz) die konventionelle Weisheit, dass es keinen Sinn macht, weil die JIT die endgültige Kompilierung durchführt), nur um den Bytecode-Countdown herunterzuholen, da Inlining-Entscheidungen auf der Bytecode-Größe basieren, nicht auf der Inline-Maschinencode-Größe (und die Korrelation kann um Größenordnungen variieren).
BeeOnRope
4

Für praktische Zwecke uint_fast32_tist völlig nutzlos. Es ist auf der am weitesten verbreiteten Plattform (x86_64) falsch definiert und bietet nirgendwo wirklich Vorteile, es sei denn, Sie haben einen Compiler von sehr geringer Qualität. Konzeptionell ist es nie sinnvoll, die "schnellen" Typen in Datenstrukturen / Arrays zu verwenden. Alle Einsparungen, die Sie durch die effizientere Bearbeitung des Typs erzielen, werden durch die Kosten (Cache-Fehler usw.) für die Vergrößerung von in den Schatten gestellt Ihr Arbeitsdatensatz. Und für einzelne lokale Variablen (Schleifenzähler, Temps usw.) kann ein Nicht-Spielzeug-Compiler normalerweise nur mit einem größeren Typ im generierten Code arbeiten, wenn dies effizienter ist, und nur dann auf die Nenngröße kürzen, wenn dies für die Korrektheit erforderlich ist (und mit signierte Typen, es ist nie notwendig).

Die eine Variante, die theoretisch nützlich ist uint_least32_t, ist , wenn Sie in der Lage sein müssen, einen beliebigen 32-Bit-Wert zu speichern, aber auf Maschinen portabel sein möchten, denen ein 32-Bit-Typ mit exakter Größe fehlt. In der Praxis ist dies jedoch kein Grund zur Sorge.

R .. GitHub HÖREN SIE AUF, EIS ZU HELFEN
quelle
4

Nach meinem Verständnis sollte intes sich ursprünglich um einen "nativen" Integer-Typ mit zusätzlicher Garantie handeln, dass er mindestens 16 Bit groß sein sollte - etwas, das damals als "vernünftige" Größe galt.

Als 32-Bit-Plattformen häufiger wurden, können wir sagen, dass sich die "angemessene" Größe auf 32 Bit geändert hat:

  • Modernes Windows verwendet 32-Bit intauf allen Plattformen.
  • POSIX garantiert intmindestens 32 Bit.
  • C #, Java hat einen Typ, intder garantiert genau 32 Bit beträgt.

Aber als die 64-Bit-Plattform zur Norm wurde, wurde niemand intzu einer 64-Bit-Ganzzahl erweitert, weil:

  • Portabilität: Viel Code hängt von intder Größe von 32 Bit ab.
  • Speicherverbrauch: Eine Verdoppelung der Speichernutzung ist intin den meisten Fällen möglicherweise nicht zumutbar, da in den meisten Fällen die verwendeten Zahlen viel kleiner als 2 Milliarden sind.

Nun, warum wollen Sie uint32_tzu uint_fast32_t? Aus dem gleichen Grund verwenden Sprachen, C # und Java immer Ganzzahlen mit fester Größe: Der Programmierer schreibt keinen Code und denkt über mögliche Größen unterschiedlicher Typen nach. Er schreibt für eine Plattform und testet Code auf dieser Plattform. Der größte Teil des Codes hängt implizit von bestimmten Größen der Datentypen ab. Und deshalb uint32_tist es in den meisten Fällen eine bessere Wahl - es lässt keine Unklarheiten hinsichtlich seines Verhaltens zu.

Ist es uint_fast32_twirklich der schnellste Typ auf einer Plattform mit einer Größe von 32 Bit oder mehr? Nicht wirklich. Betrachten Sie diesen Code-Compiler von GCC für x86_64 unter Windows:

extern uint64_t get(void);

uint64_t sum(uint64_t value)
{
    return value + get();
}

Die generierte Baugruppe sieht folgendermaßen aus:

push   %rbx
sub    $0x20,%rsp
mov    %rcx,%rbx
callq  d <sum+0xd>
add    %rbx,%rax
add    $0x20,%rsp
pop    %rbx
retq

Wenn Sie nun get()den Rückgabewert auf uint_fast32_t(unter Windows x86_64 4 Byte) ändern , erhalten Sie Folgendes:

push   %rbx
sub    $0x20,%rsp
mov    %rcx,%rbx
callq  d <sum+0xd>
mov    %eax,%eax        ; <-- additional instruction
add    %rbx,%rax
add    $0x20,%rsp
pop    %rbx
retq

Beachten Sie, dass der generierte Code fast identisch ist, mit Ausnahme zusätzlicher mov %eax,%eaxAnweisungen nach dem Funktionsaufruf, mit denen der 32-Bit-Wert in einen 64-Bit-Wert erweitert werden soll.

Es gibt kein solches Problem, wenn Sie nur 32-Bit-Werte verwenden, aber Sie werden wahrscheinlich solche mit size_tVariablen verwenden (Array-Größen wahrscheinlich?) Und diese sind 64-Bit auf x86_64. Unter Linux sind uint_fast32_tes 8 Bytes, daher ist die Situation anders.

Viele Programmierer verwenden, intwenn sie einen kleinen Wert zurückgeben müssen (sagen wir im Bereich [-32,32]). Dies würde perfekt funktionieren, wenn intes sich um eine native Ganzzahlgröße für Plattformen handelt. Da es sich jedoch nicht um eine 64-Bit-Plattform handelt, ist ein anderer Typ, der dem nativen Plattformtyp entspricht, die bessere Wahl (es sei denn, er wird häufig mit anderen Ganzzahlen kleinerer Größe verwendet).

Grundsätzlich ist, unabhängig davon, was der Standard sagt, uint_fast32_tbei einigen Implementierungen ohnehin ein Fehler aufgetreten. Wenn Sie sich für zusätzliche Anweisungen interessieren, die an einigen Stellen generiert werden, sollten Sie Ihren eigenen "nativen" Ganzzahltyp definieren. Oder Sie können es size_tfür diesen Zweck verwenden, da es normalerweise der nativeGröße entspricht (ich schließe keine alten und undurchsichtigen Plattformen wie 8086 ein, sondern nur Plattformen, auf denen Windows, Linux usw. ausgeführt werden können).


Ein weiteres Zeichen, das anzeigt, dass intes sich um einen nativen Ganzzahltyp handeln sollte, ist die "Ganzzahl-Heraufstufungsregel". Die meisten CPUs können nur Operationen auf nativen Systemen ausführen, sodass 32-Bit-CPUs normalerweise nur 32-Bit-Additionen, Subtraktionen usw. ausführen können (Intel-CPUs sind hier eine Ausnahme). Ganzzahlige Typen anderer Größen werden nur durch Lade- und Speicheranweisungen unterstützt. Beispielsweise sollte der 8-Bit-Wert mit der entsprechenden Anweisung "8-Bit-Vorzeichen laden" oder "8-Bit-Vorzeichen laden" geladen werden und wird nach dem Laden auf 32 Bit erweitert. Ohne die Ganzzahl-Heraufstufungsregel müssten C-Compiler etwas mehr Code für Ausdrücke hinzufügen, die Typen verwenden, die kleiner als der native Typ sind. Leider gilt dies bei 64-Bit-Architekturen nicht mehr, da Compiler in einigen Fällen zusätzliche Anweisungen ausgeben müssen (wie oben gezeigt).

StaceyGirl
quelle
2
Gedanken über "Niemand hat int auf 64-Bit-Ganzzahl erweitert, weil" und "Leider gilt dies bei 64-Bit-Architekturen nicht mehr" sind sehr gute Punkte . Um fair zu sein, was den "schnellsten" und den Vergleich von Assembler-Code betrifft: In diesem Fall scheint das 2. Code-Snippet mit seiner zusätzlichen Anweisung langsamer zu sein, aber Codelänge und -geschwindigkeit sind manchmal nicht so gut korreliert. Ein stärkerer Vergleich würde die Laufzeiten anzeigen - aber das ist nicht so einfach.
chux
Ich weiß nicht, dass es einfach sein wird, die Langsamkeit des zweiten Codes zu messen. Die Intel-CPU leistet möglicherweise wirklich gute Arbeit, aber längerer Code bedeutet auch eine große Cache-Verschmutzung. Hin und wieder schadet eine einzelne Anweisung wahrscheinlich nicht, aber die Nützlichkeit von uint_fast32_t wird mehrdeutig.
StaceyGirl
Ich stimme voll und ganz der Nützlichkeit von uint_fast32_tzu, die unter allen, aber sehr ausgewählten Umständen nicht eindeutig ist. Ich vermute, der treibende Grund dafür uint_fastN_tist, dass wir "nicht unsignedals 64-Bit verwenden, obwohl es auf einer neuen Plattform oft am schnellsten ist, weil zu viel Code kaputt geht", aber "Ich möchte immer noch einen schnellen, mindestens N-Bit-Typ . " Ich würde dich wieder UV, wenn ich könnte.
chux Reinstate Monica
Die meisten 64-Bit-Architekturen können problemlos mit 32-Bit-Ganzzahlen betrieben werden. Sogar DEC Alpha (eine verzweigte neue 64-Bit-Architektur anstelle einer Erweiterung einer vorhandenen 32-Bit-ISA wie PowerPC64 oder MIPS64) hatte 32- und 64-Bit-Ladevorgänge / -Speicher. (Aber keine Byte- oder 16-Bit-Ladevorgänge / Speicher!). Die meisten Anweisungen waren nur 64-Bit, aber es gab native HW-Unterstützung für 32-Bit-Add / Sub und Multiplikation, die das Ergebnis auf 32 Bit abschneiden. ( alasir.com/articles/alpha_history/press/alpha_intro.html ) Es würde also fast keinen Geschwindigkeitsgewinn durch int64-Bit und normalerweise einen Geschwindigkeitsverlust durch den Cache-Footprint geben.
Peter Cordes
Wenn Sie int64-Bit erstellt haben, benötigt Ihr uint32_tTypedef mit fester Breite einen __attribute__oder einen anderen Hack oder einen benutzerdefinierten Typ, der kleiner als ist int. (Oder short, aber dann haben Sie das gleiche Problem uint16_t.) Niemand will das. 32-Bit ist breit genug für fast alles (im Gegensatz zu 16-Bit); Die Verwendung von 32-Bit-Ganzzahlen, wenn dies alles ist, was Sie benötigen, ist auf einem 64-Bit-Computer in keiner sinnvollen Weise "ineffizient".
Peter Cordes
2

In vielen Fällen, wenn ein Algorithmus mit einem Array von Daten arbeitet, besteht der beste Weg zur Verbesserung der Leistung darin, die Anzahl der Cache-Fehler zu minimieren. Je kleiner jedes Element ist, desto mehr können in den Cache passen. Aus diesem Grund wird immer noch viel Code für die Verwendung von 32-Bit-Zeigern auf 64-Bit-Computern geschrieben: Sie benötigen keine Daten in der Nähe von 4 GB, aber die Kosten für die Erstellung aller Zeiger und Offsets erfordern acht Bytes anstelle von vier wäre erheblich.

Es gibt auch einige ABIs und Protokolle, die genau 32 Bit benötigen, z. B. IPv4-Adressen. Das uint32_tbedeutet wirklich: Verwenden Sie genau 32 Bit, unabhängig davon, ob dies für die CPU effizient ist oder nicht. Diese wurden früher als longoder deklariert unsigned long, was während des 64-Bit-Übergangs viele Probleme verursachte. Wenn Sie nur einen vorzeichenlosen Typ benötigen, der Zahlen bis zu mindestens 2³²-1 enthält, ist dies die Definition unsigned longseit Erscheinen des ersten C-Standards. In der Praxis ging jedoch genug alter Code davon aus, dass a longeinen Zeiger oder einen Dateiversatz oder einen Zeitstempel enthalten könnte, und genug alter Code davon aus, dass er genau 32 Bit breit war, sodass Compiler nicht unbedingt longdas Gleiche tun können, int_fast32_tohne zu viel Material zu beschädigen.

Theoretisch wäre die Verwendung eines Programms zukunftssicherer uint_least32_t und möglicherweise sogar uint_least32_tElemente uint_fast32_tfür Berechnungen in eine Variable zu laden . Eine Implementierung, die überhaupt keinen uint32_tTyp hatte, konnte sich sogar als formal konform mit dem Standard deklarieren! (Es wäre einfach nicht in der Lage sein , viele bestehenden Programme zu kompilieren.) In der Praxis gibt es keine Architektur mehr wo int, uint32_tund uint_least32_tist nicht gleich, und kein Vorteil, zur Zeit , zu der Leistung uint_fast32_t. Warum also zu komplizierte Dinge?

Schauen Sie sich jedoch den Grund an, warum alle 32_tTypen existieren mussten, als wir sie bereits hatten long, und Sie werden sehen, dass diese Annahmen in unseren Gesichtern bereits in die Luft gesprengt wurden. Ihr Code wird möglicherweise eines Tages auf einem Computer ausgeführt, auf dem 32-Bit-Berechnungen mit exakter Breite langsamer sind als die native Wortgröße, und Sie wären besser dran gewesenuint_least32_t für die Speicherung und uint_fast32_tfür religiöse Berechnungen zu verwenden. Oder wenn Sie diese Brücke überqueren, wenn Sie dazu kommen und nur etwas Einfaches wollen, gibt es unsigned long.

Davislor
quelle
Es gibt jedoch Architekturen mit intnicht 32 Bit, z. B. ILP64. Nicht dass sie üblich wären.
Antti Haapala
Ich glaube nicht, dass ILP64 in der Gegenwart existiert? Mehrere Webseiten behaupten, dass "Cray" es verwendet, die alle dieselbe Unix.org-Seite von 1997 zitieren, aber UNICOS Mitte der 90er Jahre tatsächlich etwas Seltsameres getan hat und die heutigen Crays Intel-Hardware verwenden. Dieselbe Seite behauptet, dass ETA-Supercomputer ILP64 verwendet haben, aber sie haben vor langer Zeit ihr Geschäft eingestellt. Wikipedia behauptet, dass HALs Solaris-Port auf SPARC64 ILP64 verwendet hat, aber sie sind auch seit Jahren nicht mehr im Geschäft. Laut CppReference wurde ILP64 nur in einigen frühen 64-Bit-Unices verwendet. Es ist also nur für einige sehr esoterische Retrocomputing relevant.
Davislor
Wenn Sie heute die „ILP64-Schnittstelle“ der Intel Math Kernel Library verwenden, intist diese 32 Bit breit. Der Typ MKL_INTwird sich ändern.
Davislor
1

Um eine direkte Antwort zu geben: Ich denke, der wahre Grund, warum uint32_tüber uint_fast32_toder uint_least32_teinfach verwendet wird, ist, dass es einfacher zu tippen ist und aufgrund der Kürzung viel besser zu lesen ist: Wenn Sie Strukturen mit einigen Typen erstellen, und einige von ihnen sind uint_fast32_toder ähnlich, dann ist es oft schwierig, sie gut mit intoder booloder anderen Typen in C auszurichten , die ziemlich kurz sind (Beispiel: charvs. character). Ich kann dies natürlich nicht mit harten Daten belegen, aber die anderen Antworten können auch nur den Grund erraten.

Aus technischen Gründen uint32_tgibt es meines Erachtens keine - wenn Sie unbedingt ein genaues 32-Bit-Int ohne Vorzeichen benötigen , ist dieser Typ Ihre einzige standardisierte Wahl. In fast allen anderen Fällen sind die anderen Varianten technisch vorzuziehen - insbesondere, uint_fast32_twenn Sie sich Gedanken über die Geschwindigkeit und den uint_least32_tSpeicherplatz machen. Mit uint32_tin beiden Fällen der Fähigkeit , Risiken zu kompilieren nicht als der Typ nicht existieren erforderlich ist.

In der Praxis existieren die uint32_tund verwandte Typen auf allen aktuellen Plattformen, mit Ausnahme einiger sehr seltener (heutzutage) DSPs oder Scherzimplementierungen, sodass bei der Verwendung des genauen Typs nur ein geringes tatsächliches Risiko besteht. Während Sie mit den Typen mit fester Breite auf Geschwindigkeitseinbußen stoßen können, sind sie (bei modernen CPUs) nicht mehr lähmend.

Aus diesem Grund, denke ich, gewinnt der kürzere Typ in den meisten Fällen einfach aufgrund der Faulheit des Programmierers.

Erinnere dich an Monica
quelle