Wie erkennt man die Maschinenwortgröße in C/C++?

Lesezeit: 10 Minuten

Benutzer-Avatar
rostig

Gibt es einen mehr oder weniger zuverlässigen Weg (nicht unbedingt perfekt), um das zu erkennen? Maschinenwortgröße des Zielarchitektur für die ich kompiliere?

Durch Maschinenwortgröße Ich meine die Größe des ganzzahligen Akkumulatorregisters (z. B. EAX auf x86, RAX auf x86_64 usw., nicht Streaming-Erweiterungen, Segment- oder Gleitkommaregister).

Der Standard scheint keinen Datentyp “Maschinenwort” bereitzustellen. Also bin ich nicht auf der Suche nach einem 100 % tragbaren Weg, nur etwas, das in den meisten gängigen Fällen funktioniert (Intel x86 Pentium+, ARM, MIPS, PPC – also registerbasierte, zeitgemäße Commodity-Prozessoren).

size_t und uintptr_t klingen wie gute Kandidaten (und haben in der Praxis überall, wo ich getestet habe, die Registergröße erreicht), sind aber natürlich etwas anderes und werden daher nicht garantiert immer so sein, wie bereits in Is size_t the word size beschrieben.

Kontext

Nehmen wir an, ich implementiere eine Hashing-Schleife über einen Block zusammenhängender Daten. Es ist in Ordnung, dass der resultierende Hash vom Compiler abhängt, nur die Geschwindigkeit zählt.

Beispiel: http://rextester.com/VSANH87912

Tests unter Windows zeigen, dass das Hashing in 64-Bit-Blöcken im 64-Bit-Modus und in 32-Bit im 32-Bit-Modus schneller ist:

64-bit mode
int64: 55 ms
int32: 111 ms

32-bit mode
int64: 252 ms
int32: 158 ms

  • Mögliches Duplikat von Ist size_t die Wortgröße?

    – hdl

    7. März 2016 um 12:12 Uhr

  • Sie können immer noch Informationen von Compiler und Architektur mischen, die bestimmte Makros haben.

    – Jarod42

    7. März 2016 um 12:14 Uhr

  • @stark sizeof(long) ist 4 (32 Bit) im 64-Bit-Windows-Datenmodell.

    – zwischenjay

    7. März 2016 um 12:26 Uhr

  • Zum Kontext – die Frage ist ziemlich klar – wie kann ich die Maschinenwortgröße bestimmen? Die Antwort kann dann auf eine Vielzahl unterschiedlicher Kontexte angewendet werden.

    – rostig

    7. März 2016 um 13:45 Uhr

  • Warum solltest du das wissen? Was möchten Sie mit diesen Informationen tun? Wenn ich Ihnen auf einer Plattform eine falsche Wortgröße mitteilen würde, wie würde sich dies auf das auswirken, was Sie zu tun versuchen?

    – n. 1.8e9-wo-ist-meine-Aktie m.

    7. März 2016 um 14:42 Uhr

Benutzer-Avatar
Eduard

Da die Sprachen C und C++ Überlegungen wie die Maschinenwortgröße absichtlich abstrahieren, ist es unwahrscheinlich, dass eine Methode zu 100 % zuverlässig ist. Allerdings gibt es die verschiedenen int_fastXX_t Typen, die Ihnen helfen können, die Größe abzuleiten. Zum Beispiel dieses einfache C++-Programm:

#include <iostream>
#include <cstdint>

#define SHOW(x) std::cout << # x " = " << x << '\n'

int main()
{
    SHOW(sizeof(int_fast8_t));
    SHOW(sizeof(int_fast16_t));
    SHOW(sizeof(int_fast32_t));
    SHOW(sizeof(int_fast64_t));
}

erzeugt dieses Ergebnis mit gcc Version 5.3.1 auf meinem 64-Bit-Linux-Rechner:

sizeof(int_fast8_t) = 1
sizeof(int_fast16_t) = 8
sizeof(int_fast32_t) = 8
sizeof(int_fast64_t) = 8

Dies deutet darauf hin, dass ein Mittel zum Ermitteln der Registergröße darin bestehen könnte, nach dem größten Unterschied zwischen einer erforderlichen Größe (z. B. 2 Bytes für einen 16-Bit-Wert) und der entsprechenden Größe zu suchen int_fastXX_t Größe und mit der Größe der int_fastXX_t als Registergröße.

Weitere Ergebnisse

Windows 7, gcc 4.9.3 unter Cygwin auf 64-Bit-Maschine: wie oben

Windows 7, Visual Studio 2013 (v 12.0) auf einem 64-Bit-Computer:

sizeof(int_fast8_t) = 1
sizeof(int_fast16_t) = 4
sizeof(int_fast32_t) = 4
sizeof(int_fast64_t) = 8

Linux, gcc 4.6.3 auf 32-Bit-ARM und auch Linux, gcc 5.3.1 auf 32-Bit-Atom:

sizeof(int_fast8_t) = 1
sizeof(int_fast16_t) = 4
sizeof(int_fast32_t) = 4
sizeof(int_fast64_t) = 8

  • Schöner Vorschlag. Schade, dass dies unter Windows nicht funktioniert (druckt die gleichen Werte auf 32-Bit- und 64-Bit-Plattformen).

    – rostig

    7. März 2016 um 14:08 Uhr

  • @rustyx: Du hast recht. Ich habe meine Antwort hinzugefügt, um das zu zeigen.

    – Eduard

    7. März 2016 um 14:31 Uhr

  • Ihr Ergebnis kann das Ergebnis dessen sein, welchen Compiler Sie verwendet haben. Es ist möglich, einen 32-Bit-Compiler auf einem 64-Computer zu verwenden. Das Ergebnis wird ausgeführt.

    – Robert Jacobs

    7. März 2016 um 14:40 Uhr

  • @RobertJacobs: Das Ergebnis ist völlig das Ergebnis, welcher Compiler verwendet wird, es handelt sich also eher um einen Compilertest als um einen CPU-Test.

    – Eduard

    7. März 2016 um 14:41 Uhr

  • @Edward Deshalb habe ich Ihre Compilereinstellungen im Windows 7 Visual Studio 2013-Test gefragt. Ist das Ergebnis eine ausführbare 32- oder 64-Bit-Datei?

    – Robert Jacobs

    7. März 2016 um 15:00 Uhr

Benutzer-Avatar
Robert Jacobs

Ich denke, du willst

sizeof(size_t) was die Größe eines Index haben soll. dh. ar[index]

32 bit machine

char 1
int 4
long 4
long long 8
size_t 4

64 bit machine

char 1
int 4
long 8
long long 8
size_t 8

Es kann komplizierter sein, da 32-Bit-Compiler auf 64-Bit-Computern ausgeführt werden. Ihre Leistung 32, obwohl die Maschine mehr kann.

Ich habe Windows-Compiler unten hinzugefügt

Visual Studio 2012 compiled win32

char 1
int 4
long 4
long long 8
size_t 4

Visual Studio 2012 compiled x64

char 1
int 4
long 4
long long 8
size_t 8

  • Welche würde ich also als Maschinenwortgröße verwenden?

    – rostig

    7. März 2016 um 14:09 Uhr

  • Ich würde mit size_t gehen, da der Array-Zugriff von Natur aus eine Registeroperation ist.

    – Robert Jacobs

    7. März 2016 um 14:13 Uhr

  • size_t soll “groß genug sein, um die Größe jedes Objekts in Bytes zu enthalten” [support.types]/6. Es hat nichts mit Array-Zugriff zu tun.

    – Peter Becker

    7. März 2016 um 15:48 Uhr

  • @RobertJacobs – ja, malloc nimmt ein Argument vom Typ an size_t. Das ist, weil size_t kann die Größe eines beliebigen Objekts darstellen; das fordert die Norm, weshalb ich die Norm zitiert habe. Bei der Array-Indizierung geht es um Zeiger Bereiche; wenn Sie in ein Array von indizieren char Ihr Indextyp sollte besser groß genug sein, um die maximale Größe von a aufzunehmen char Array, selbst wenn ein einzelnes Register nicht groß genug ist, um einen solchen Indexwert aufzunehmen.

    – Peter Becker

    7. März 2016 um 16:17 Uhr


  • Sobald Sie einen 32-Bit-Compiler auf 64-Bit-Hardware ausführen, müssen Sie sich fragen: “Was stellt die Frage überhaupt?” und warum?”.

    – Hartnäckig

    23. Januar 2018 um 15:03 Uhr

Benutzer-Avatar
Serge Ballesta

Auch in der Maschinenarchitektur a Wort können mehrere Dinge sein. AFAIK Sie haben unterschiedliche hardwarebezogene Mengen:

  • Zeichen: im Allgemeinen ist es das kleinste Element, das zum oder vom Speicher ausgetauscht werden kann – es ist jetzt fast überall 8 Bit, aber früher waren es 6 auf einigen älteren Architekturen (CDC in den frühen 80ern)
  • integer: ein Integer-Register (zB EAX auf einem x86). IMHO ist eine akzeptable Annäherung sizeof(int)
  • Adresse: was auf der Architektur angesprochen werden kann. IMHO ist eine akzeptable Annäherung sizeof(uintptr_t)
  • nicht die Rede von Gleitkommazahlen …

Lassen Sie uns etwas Geschichte schreiben:

Machine class     |   character    |  integer    | address
-----------------------------------------------------------
old CDC           |     6 bits     |    60 bits  |  ?
8086              |     8 bits     |    16 bits  |  2x16 bits(*)
80x86 (x >= 3)    |     8 bits     |    32 bits  |  32 bits
64bits machines   |     8 bits     |    32 bits  |  64 bits    
                  |                |             |
general case(**)  |     8 bits     | sizeof(int) | sizeof(uintptr_t)

Es war ein spezieller Adressierungsmodus, bei dem das hohe Wort nur um 8 Bit verschoben wurde, um eine 20-Bit-Adresse zu erzeugen – aber Far-Pointer waren früher 32 Bit lang

(**) uintptr_t macht auf alter Architektur nicht viel Sinn, da die Compiler (sofern sie existierten) diesen Typ nicht unterstützten. Aber wenn ein anständiger Compiler darauf portiert wurde, gehe ich davon aus, dass die Werte so wären. AberIN ACHT NEHMEN sizeof(int) = 16 : Die Typen werden vom Compiler definiert, nicht von der Architektur. Das heißt, wenn Sie einen 8-Bit-Compiler auf einem 64-Computer finden würden, würden Sie ihn wahrscheinlich bekommen sizeof(uintptr_t) = 16und . Das Obige macht also nur Sinn, wenn Sie einen Compiler verwenden angepasst

  • sizeof(int) zur architektur…

    ist keine akzeptable Annäherung auf x86_64. Dort sind es immer noch 4, während RAX 8 Bytes ist.

    – rostig

  • 8. März 2016 um 10:15 Uhr

    @rustyx x86_64 ist eine einzelne Architektur mit festen ganzzahligen Akkumulationsregistergrößen. Von welcher Annäherung an x86_64 sprichst du? 😀

    – Jotik

8. März 2016 um 11:02 Uhr
Benutzer-Avatar

Salman Stern

Ich gebe Ihnen die richtige Antwort auf die Frage, die Sie sich stellen sollten:

F: Wie wähle ich die schnellste Hash-Routine für eine bestimmte Maschine aus, wenn ich keine bestimmte verwenden muss und sie nicht gleich sein muss, außer innerhalb eines einzelnen Builds (oder vielleicht einer Ausführung) einer Anwendung? #ifdefA: Implementieren Sie eine parametrisierte Hash-Routine, möglicherweise unter Verwendung einer Vielzahl von Grundelementen, einschließlich SIMD-Anweisungen. Auf einer bestimmten Hardware funktionieren einige davon, und Sie möchten diesen Satz mit einer Kombination aus Kompilierzeit aufzählen cpuinfo s und dynamische CPU-Funktionserkennung. (Zum Beispiel können Sie AVX2 nicht auf einem ARM-Prozessor verwenden, der zur Kompilierzeit bestimmt wird, und Sie können es nicht auf einem älteren x86 verwenden, bestimmt durch die memcpy Anweisung.) Nehmen Sie das Set, das funktioniert, und messen Sie es anhand von Testdaten auf den Maschinen, die Sie interessieren. Tun Sie dies entweder dynamisch beim System-/Anwendungsstart oder testen Sie so viele Fälle wie möglich und codieren Sie fest, welche Routine auf welchem ​​System basierend auf einem Sniffing-Algorithmus verwendet werden soll. (Der Linux-Kernel tut dies zB, um den schnellsten zu ermitteln

Alltag usw.)

Die Umstände, unter denen der Hash konsistent sein muss, sind anwendungsabhängig. Wenn Sie die Auswahl vollständig zur Kompilierzeit treffen müssen, müssen Sie eine Reihe von Präprozessormakros erstellen, die der Compiler definiert. Oft ist es möglich, mehrere Implementierungen zu haben, die denselben Hash erzeugen, aber unterschiedliche Hardwareansätze für unterschiedliche Größen verwenden.

Das Überspringen von SIMD ist wahrscheinlich keine gute Idee, wenn Sie einen neuen Hash definieren und möchten, dass er wirklich schnell ist, obwohl es in einigen Anwendungen möglich sein kann, die Speichergeschwindigkeit ohne die Verwendung von SIMD zu sättigen, sodass es keine Rolle spielt. size_t Wenn das alles nach zu viel Arbeit klingt, verwenden Sie std::atomic als Akkugröße. Oder verwenden Sie die größte Größe für die std::atomic_is_lock_freesagt Ihnen, dass der Typ frei von Sperren ist. Sehen: std::atomic::is_lock_free, std::atomic::is_always_lock_freeoder

.

Unter “Maschinenwortgröße” müssen wir annehmen, dass die Bedeutung ist: die größte Größe eines Datenstücks, das die CPU in einer einzigen Anweisung verarbeiten kann. (Manchmal als Datenbusbreite bezeichnet, obwohl dies eine Vereinfachung ist.) size_tAuf verschiedenen CPUs, uintptr_t , ptrdiff_t und könnte alles sein – diese beziehen sich auf dieAdressbusbreite

, und nicht die CPU-Datenbreite. Also können wir diese Typen vergessen, sie sagen uns nichts. char Auf allen Mainstream-CPUs, short ist immer 8 Bit, long long ist immer 16 Bit und int ist immer 64 Bit. Die einzigen interessanten Typen, die übrig bleiben, sind also longund


.

Die folgenden Mainstream-CPUs existieren:

int   = 16 bits   
long  = 32 bits

8 Bit

int   = 16 bits   
long  = 32 bits

16 Bit

int   = 32 bits   
long  = 32 bits

32 Bit

int   = 32 bits   
long  = 32 bits

64 Bit

Es kann unkonventionelle Variationen des oben Gesagten geben, aber im Allgemeinen lässt sich aus dem Obigen nicht sagen, wie man 8-Bit von 16-Bit oder 32-Bit von 64-Bit unterscheidet.

Alignment hilft uns auch nicht weiter, da es für verschiedene CPUs gelten kann oder auch nicht. Viele CPUs können falsch ausgerichtete Wörter gut lesen, aber auf Kosten langsameren Codes.


Es gibt also keine Möglichkeit, die “Maschinenwortgröße” mit Standard-C zu bestimmen. stdint.hEs ist jedoch möglich, voll portierbares C zu schreiben, das auf allem zwischen 8 und 64 Bit laufen kann, indem Sie die Typen von verwenden uint_fast insbesondere die

  • Typen. Einige Dinge, die Sie beachten sollten, sind: uint32_t Implizite Integer-Promotions über verschiedene Systeme hinweg. Alles von
  • oder größer ist im Allgemeinen sicher und tragbar. intDer Standardtyp von Integer-Konstanten (“Literale”). Dies ist meistens (aber nicht immer) int und was für ein
  • auf einem bestimmten System ist, kann variieren.
  • Ausrichtung und Struct/Union-Padding.

Die Zeigergröße ist nicht notwendigerweise die gleiche wie die Maschinenwortgröße.  Dies gilt insbesondere für viele 8-, 16- und 64-Bit-Computer.
Benutzer-Avatar

Malcolm McLean

Wählen Sie sizeof(int *) * CHAR_BIT, um die Größe der Maschinenarchitektur in Bits zu erhalten.

1239880cookie-checkWie erkennt man die Maschinenwortgröße in C/C++?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy