Sinn und Zweck von int



  • volkard schrieb:

    Leider gibt es damit keinen Typ mehr, von dem man stark annehmen sagen kann, daß er die Registerbreite gerade ausschöpft. Schade.

    Theoretisch dürfte (u)int_fast16_t aus <cstdint> dies erfüllen, oder? Ob er tatsächlich so umgesetzt ist, ist wieder eine andere Frage. Eventuell muss man sich selbst was basteln...



  • long entspricht doch auf praktisch jedem System außer Windows einem Machineword, also Registerbreite ?



  • Ethon schrieb:

    long entspricht doch auf praktisch jedem System außer Windows einem Machineword, also Registerbreite ?

    Genau das sagte man früher über den int.



  • Nexus schrieb:

    volkard schrieb:

    Leider gibt es damit keinen Typ mehr, von dem man stark annehmen sagen kann, daß er die Registerbreite gerade ausschöpft. Schade.

    Theoretisch dürfte (u)int_fast16_t aus <cstdint> dies erfüllen, oder? Ob er tatsächlich so umgesetzt ist, ist wieder eine andere Frage. Eventuell muss man sich selbst was basteln...

    Ich würde eher für logisch halten, daß der uint_fast_16_t der kleinste typ ist, der nicht langsamer rechnet als ein typ mit mehr als 16 bit. ich stelle mir maschinen wie frühe pentiums vor, die 32-bittig schneller rechnen konnten als 16-bittig. oder ganz andere maschinen, wo nur 32-bittig geht und 16-bittige rechnungen durch anschließendes &0xffff simuliert werden müssen.


  • Mod

    Obligatorischer Hinweis, dass ein Byte nur mindestens 8 Bit hat und theoretisch auch größer sein darf. Vor längerer Zeit hat sogar mal jemand ein konkretes Beispiel für einen solchen Rechner genannt.



  • volkard schrieb:

    Sowohl auf 32 Bit, als auch auf 64 Bit ist int 4 Byte lang und long long 8 Byte. Aber long ist entweder 4 oder 8 Byte lang.

    Nein, das ist falsch.
    Die ISO Norm gibt nur Mindestgrößen vor.

    sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof (long) = sizeof(size_t)
    

    Die ISO C++ Norm verweist auf die ISO C Norm für die Datentypengrößen. <limits.h> definiert für

    • char min. 8Bit
    • short min. 16Bit
    • int min. 16Bit
    • long min. 32Bit
    • long long min. 64Bit

    Die ISO Norm für POSIX und die Single UNIX Specification definieren einen 32Bit und einen 64Bit Modus, die als ILP32 bzw. LP64 festgelegt sind. Windows nutzt ILP32 und ILP64.



  • Dann wünsch ich mir für C++0x eben, dass das Mal sinnvoll gemacht wird. Dass dies größer als das ist, ist ja ganz nett, aber wenn ich eben unabhängig von der Größe möglichst performant arbeiten möchte, ist das anscheinend x86-x64-übergreifend nicht geklärt, richtig?


  • Mod

    Eisflamme schrieb:

    Dann wünsch ich mir für C++0x eben, dass das Mal sinnvoll gemacht wird. Dass dies größer als das ist, ist ja ganz nett, aber wenn ich eben unabhängig von der Größe möglichst performant arbeiten möchte, ist das anscheinend x86-x64-übergreifend nicht geklärt, richtig?

    Naja, 32 Bit zu verrechnen muss auf 64 Bit nicht unbedingt langsamer sein als 64-Bit Zahlen zu verrechnen. Ich würde sogar eher erwarten, dass es schneller ist. Du kannst davon ausgehen, dass intern ein größerer Datentyp benutzt würde, wenn dieser wesentliche Geschwindigkeitsvorteile böte, siehe dazu auch die short-Debatte. Der Vorteil von 64-Bit Zahlen auf 64-Bit Rechnern ist ja, dass diese nicht so lahm sind wie auf 32-Bit. Dadurch werden die kleineren Zahlen nicht langsamer.



  • Ich verstehe. Und Alignment sorgt z.B. innerhalb von Strukturen für genau das, was Du da beschreibst, oder?

    also nur zum Gesamtverständnis für mich, hat nix mit den Datentypen oder dem Thema hier zu tun


  • Mod

    Eisflamme schrieb:

    Ich verstehe. Und Alignment sorgt z.B. innerhalb von Strukturen für genau das, was Du da beschreibst, oder?

    Solche Details der Mikrooptimierung überlasse ich dem Compiler. Ich weiß bloß, dass es solche Mechanismen gibt, aber ich weiß nicht genug um qualifizierte Aussagen über die Details zu machen.
    Da Geschwindigkeitsoptimierungen aber ein gängiger Schwanzvergleich zwischen verschiedenen Compilern sind, darfst du davon ausgehen, dass solche Details ziemlich optimal umgesetzt werden. Wenn es da etwas zu holen gäbe, würde es auch gemacht.
    Ich kann ja mal einen Benchmark machen, während ich meinen Kaffee trinke. Ich melde mich gleich wieder. Aber wenn die 64-Bits gewinnen, würde mich das sehr überraschen.



  • Mich hat nur der Hintergrund von Alignment interessiert, da es für mich gerade logisch erscheint, dass auf Registerbreiten optimiert wird. Alignment hat ja wohl oft 4 Byte, was 32 Bit entspricht. Kompiliert man für 64 Bit, wäre ein Alignment auf 8 Byte wohl logisch. 🙂 Benchmarking klingt gut!


  • Mod

    So, ich habe da was zusammengehackt. Leider erstmal nur für POSIX, da ich gerade keine Zeit hatte, boost::ptime zu lernen:

    #include <iostream>
    #include <ctime>
    
    using namespace std;
    
    struct Foo
    {
      int a;
      int b;
    };
    
    struct Bar
    {
      long a;
      long b;
    };
    
    template<typename T> void waste_time(decltype(T::a) how_much)
    {
      typedef decltype(T::a) type;
    
      timespec start, end;
      clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
    
      T t={12345, 54321};
      for(type i=0; i<how_much; ++i)
        {
          t.a+=t.b;
          t.b*=t.b;
          t.a-=t.b;
          t.b^=t.a;
          t.a<<=4;
          t.b-=t.a;
          t.a>>=3;
          t.b+=7;
          t.a+=t.b;
        }
      clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);
    
      cout<<"Anti-Optimierungsausgabe: "<<t.a<<' '<<t.b<<'\n';
      long long nano=(end.tv_sec - start.tv_sec) * 1000000000;
      nano+=(end.tv_nsec - start.tv_nsec);
    
      cout<<"Benötigte Nanosekunden: "<<nano<<"\n\n";
    
    }
    
    int main()
    {
      cout << "Sizeof int: " << sizeof(int) <<'\n'
           << "Sizeof long: " << sizeof(long) <<'\n'
           << "Sizeof Foo: " << sizeof(Foo) <<'\n'
           << "Sizeof Bar: " << sizeof(Bar) <<'\n';
    
      cout<<"\nEine dem Compiler unbekannte Zahl: ";
      int anti_optimizer_input;
      cin>>anti_optimizer_input;
    
      cout<<"Mit Foo:\n";
      waste_time<Foo>(anti_optimizer_input);
      cout<<"Mit Bar:\n";
      waste_time<Bar>(anti_optimizer_input);
      cout<<"Nochmal mit Foo:\n";
      waste_time<Foo>(anti_optimizer_input);
      cout<<"Nochmal mit Bar:\n";
      waste_time<Bar>(anti_optimizer_input);
      cout<<"10x mit Foo:\n";
      waste_time<Foo>(10*anti_optimizer_input);
      cout<<"10x mit Bar:\n";
      waste_time<Bar>(10*anti_optimizer_input);
      cout<<"10x mit Foo:\n";
      waste_time<Foo>(10*anti_optimizer_input);
      cout<<"10x mit Bar:\n";
      waste_time<Bar>(10*anti_optimizer_input);
    }
    

    Die Ergebnisse sind konsistent über viele Läufe auf meinem Core 2:

    [b]seppj@irgendwo{~/src}> [/b]g++ test.cc -Wall -std=c++0x -O3 -Wextra -xHost -lrt
    [b]seppj@irgendwo{~/src}>[/b] ./a.out
    Sizeof int: 4
    Sizeof long: 8
    Sizeof Foo: 8
    Sizeof Bar: 16
    
    Eine dem Compiler unbekannte Zahl: 10000000
    Mit Foo:
    Anti-Optimierungsausgabe: -1332682501 -1166096675
    Benötigte Nanosekunden: 50591275
    
    Mit Bar:
    Anti-Optimierungsausgabe: -1527253877453168389 -1903512469154904355
    Benötigte Nanosekunden: 55345337
    
    Nochmal mit Foo:
    Anti-Optimierungsausgabe: -1332682501 -1166096675
    Benötigte Nanosekunden: 40838216
    
    Nochmal mit Bar:
    Anti-Optimierungsausgabe: -1527253877453168389 -1903512469154904355
    Benötigte Nanosekunden: 55354001
    
    10x mit Foo:
    Anti-Optimierungsausgabe: -1332682501 -1166096675
    Benötigte Nanosekunden: 408339301
    
    10x mit Bar:
    Anti-Optimierungsausgabe: -1527253877453168389 -1903512469154904355
    Benötigte Nanosekunden: 553385774
    
    10x mit Foo:
    Anti-Optimierungsausgabe: -1332682501 -1166096675
    Benötigte Nanosekunden: 408321611
    
    10x mit Bar:
    Anti-Optimierungsausgabe: -1527253877453168389 -1903512469154904355
    Benötigte Nanosekunden: 553377433
    

    Klarer Sieg für int. Wer Spaß hat, kann ja noch ein paar Dummy-chars in die structs einsetzen, um das Alignment und Padding zu testen.


  • Mod

    Und jetzt habe ich das gleiche Programm auf einem i7 ausgeführt und nun sind int und long gleich schnell.

    Und wenn ich 32-Bit Code erzeuge gehen die 64-Bit Zahlen total ein und sind 4x so langsam wie die 32 Bit Zahlen.

    Short verkackt gegen beide und ist ungefähr 1.8 mal langsamer (auf dem i7)

    Schlussfolgerungen:
    32 Bit Systeme: 32 Bit Zahlen liegen ganz klar vorne. Offensichtlich.
    64 Bit Systeme: Kommt auf das System an, aber 32 Bit Zahlen sind jedenfalls nicht langsamer. Die 64 Bit Zahlen bieten mehr berechnete Stellen pro Zeit (auch offensichtlich), aber wenn man diese Stellen gar nicht benötigt, fährt man im Zweifelsfall mit 32 Bit Zahlen besser
    Beide: Short nur wenn man wirklich Speicherplatz sparen muss.



  • ~john schrieb:

    volkard schrieb:

    Sowohl auf 32 Bit, als auch auf 64 Bit ist int 4 Byte lang und long long 8 Byte. Aber long ist entweder 4 oder 8 Byte lang.
    ...
    Wobei das nicht gesetzlich garantiert ist, sonder nur üblich.

    Nein, das ist falsch.

    Warum widersprichst Du hier? Konnte Deiner Erklärung nicht folgen, wo Du nur aus der Norm rausholst, daß es nicht garantiert ist.



  • SeppJ schrieb:

    Klarer Sieg für int. Wer Spaß hat, kann ja noch ein paar Dummy-chars in die structs einsetzen, um das Alignment und Padding zu testen.

    Kommt das nur mir so vor oder sind die Laufzeiten und Ergebnisse für "mit Foo" und "10x mit Foo" sehr nahe beieinander? Ich hab' zwar keine Ahnung, was deine Funktion eigentlich berechnet, aber zumindest bei der Laufzeit hätte ich da größere Abweichungen erwartet.


  • Mod

    CStoll schrieb:

    SeppJ schrieb:

    Klarer Sieg für int. Wer Spaß hat, kann ja noch ein paar Dummy-chars in die structs einsetzen, um das Alignment und Padding zu testen.

    Kommt das nur mir so vor oder sind die Laufzeiten und Ergebnisse für "mit Foo" und "10x mit Foo" sehr nahe beieinander? Ich hab' zwar keine Ahnung, was deine Funktion eigentlich berechnet, aber zumindest bei der Laufzeit hätte ich da größere Abweichungen erwartet.

    😕 Hast du eine Stelle nicht mitgezählt? Es ist doch fast perfekt ein Faktor 10! Eine bessere Bestatigung kann ich mir gar nicht vorstellen, dass die Funktion das macht was sie soll.

    edit: Oder meinst du, dass du größere Schwnakungen erwartest? Wieso? Ich messe CPU-Zeit, nicht Realzeit.



  • SeppJ schrieb:

    CStoll schrieb:

    SeppJ schrieb:

    Klarer Sieg für int. Wer Spaß hat, kann ja noch ein paar Dummy-chars in die structs einsetzen, um das Alignment und Padding zu testen.

    Kommt das nur mir so vor oder sind die Laufzeiten und Ergebnisse für "mit Foo" und "10x mit Foo" sehr nahe beieinander? Ich hab' zwar keine Ahnung, was deine Funktion eigentlich berechnet, aber zumindest bei der Laufzeit hätte ich da größere Abweichungen erwartet.

    😕 Hast du eine Stelle nicht mitgezählt? Es ist doch fast perfekt ein Faktor 10! Eine bessere Bestatigung kann ich mir gar nicht vorstellen, dass die Funktion das macht was sie soll.

    Sorry, mein Fehler *brille putzen geht*. Eventuell solltest du die Ausgabe besser formatieren, so daß man den Unterschied leichter erkennen kann 😉

    (PS: Hat die Berechnung eigentlich einen praktischen Nutzen oder geht es nur darum, dort möglichst viele verschiedenen Ganzzahl-Operationen zu kombinieren?)



  • volkard schrieb:

    Warum widersprichst Du hier?

    Die Behauptung "Sowohl auf 32 Bit, als auch auf 64 Bit ist int 4 Byte lang und long long 8 Byte." ist falsch. Die ISO Norm garantiert nur, daß ein int mindestens 16Bit groß ist. Mehr wird dazu in der ISO Norm nicht garantiert. Von einem 32Bit oder 64Bit Modus ist in der ISO Norm keine Rede. Aber selbst wenn man von den üblichen Betriebssystemen ausgeht stimmt Deine Aussage auch nicht, denn es gibt dort ILP64 Modi.


  • Mod

    CStoll schrieb:

    (PS: Hat die Berechnung eigentlich einen praktischen Nutzen oder geht es nur darum, dort möglichst viele verschiedenen Ganzzahl-Operationen zu kombinieren?)

    Letzteres. Wobei ich Division vermieden habe, denn wenn man das ohne Plan macht, dann tendieren die Ergebnisse dazu, gegen 0 zu konvergieren. Zumindest ist nun festgestellt, dass bei modernen CPUs 64 und 32 Bit Register ungefähr gleich schnell sind und bei älteren Modellen die 32 Bit Teile schneller sein können (eventuell emulieren die modernen CPUs einfach die 32 Bit, aber das ist Spekulation).

    Ich habe übrigens mal den Effekt von Packing und Alignment ausprobiert. Zumindest bei diesem Programm mach Misalignment keinen Unterschied. Das liegt aber wohl daran, dass alles in einem kleinen Loop stattfindet, so dass die Werte sowieso die ganze Zeit in Prozessorregistern stehen.



  • Übrigens, es gibt Systeme (C64x DSPs von Texas Instruments oder Blackfin DSPs von Analog Devices), auf denen ein sizeof(long) gleich 5 ist. Ja, 40 Bit breit. Normale Register sind 32 Bit breit und sizeof(int) ist 4 und es gibt 40 Bit breite Register für "multiply accumulate" o.ä. Operationen.


Anmelden zum Antworten