Sinn und Zweck von int
-
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?
-
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
-
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!
-
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: 553377433Klarer 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.
-
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.
-
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.
-
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.
-
int ist kurz zu schreiben. Das ist ein Grund ihn zu verwenden.
-
SeppJ schrieb:
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).
Das liegt einfach daran, dass "die älteren Teile" keine 64 Bit Befehle haben, und daher 64 Bit Operationen aus 32 Bit Operationen zusammenstückeln müssen. Dass das langsamer ist sollte jetzt keinen wundern.
Auch ein i7 ist mit 32 Bit Code bei 64 Bit Berechnungen viel langsamer als bei 32 Bit Berechnungen. Oder als mit 64 Bit Code bei 64 Bit Berechnungen.
Ist jetzt irgendwie keine Überraschung.Oder hab' ich dich falsch verstanden?
-
hustbaer schrieb:
Oder hab' ich dich falsch verstanden?
Ich glaube ja. Das Ergebnis war:
-
Alter 64 Bit Prozessor (Core 2 Duo):
-
32 Bit Befehlssatz:
-
32-Bit Berechnungen sind viel schneller als 64-Bit, ca. Faktor 4
-
64 Bit Befehlssatz:
-
32-Bit Berechnungen sind deutlich schneller als 64-Bit, ca. Faktor 2
-
Moderner 64-Bit Prozessor (i7):
-
32 Bit Befehlssatz:
-
32-Bit Berechnungen sind viel schneller als 64-Bit, ca. Faktor 4
-
64 Bit Befehlssatz:
-
32-Bit Berechnungen sind ziemlich genau gleich schnell wie 64-Bit, ca. Faktor 1
Diese Faktoren sind ziemlich exakt. Da ich mir nicht vorstellen kann, dass so Zahlen wie 1, 2 und 4 zufällig Zustande kommen, nehme ich mal an, dass da mehr dahinter steckt.
Der Faktor 4 ist irgendwie intuitiv. Man hat in einem Fall 2 Werte mit 32-Bit, und im anderen 2x2 Werte zu je 32-Bit. Wenn die Rechnungen linear in der Größe der beiden Eingaben sind (es sind ziemlich triviale Operationen, daher ist dies gut möglich), hat man einen Faktor 4.
Aber irgendetwas haben sie gemacht, dass entweder der 32-Bit Teil des Core 2 sehr viel besser ist als der 64-Bit Teil, oder der 32-Bit Teil des i7 genauso "schlecht" ist wie der 64-Bit Teil. Ich tippen auf letzteres. Möglicherweise ist beim i7 alles 64-Bit und der Core 2 hatte noch eine reine 32-Bit Untereinheit. Aber ohne die Optimierungsrichtlinien von Intel durchzulesen (worauf ich keine Lust habe), wird man das wohl nicht sicher rausfinden.
-
-
Also ich hatte das Gefühl:
Addition und Subtraktion geht in allen Größen in einem Takt.
Multiplikation dauert um so länger, je breiter die Operanden, aber ist nicht wild. Hatte man einen AMD64, hatte man mit#define FASTMULdas gesagt, nämlich daß der Prozessor die integer-Miltiplikation in 6 oder 8 Takten durchrotzen kann. Intel schneckte da ab. Ich gehe davon aus, daß Intel inzwischen aufgeholt hat.
Bei der Integer-Division ist noch kein Land in Sicht. Größere Operanden brauchen deutlich mehr Zeit.