Programmieren - alles kontrollieren 4.941 Themen, 20.715 Beiträge

Zeit für Integer/Floatrechnungen

RyoOhki / 28 Antworten / Baumansicht Nickles

Eine Frage zur Berechnung von Integer und Float werten unter C++:

sollte ein Programm länger brauchen, zwei Float zu addieren (z.B. via Add = 0,1 + 0,1) als zwei integerwerte (Add2 = 1 + 1 ) zu addieren, da es sich um Flieskommberechnungen handel?

ich habe ein Programm geschrieben das beide opperationen 40000000000mal ausführt und die zeit dafür stoppt, erhalte aber bei beiden beinahe identische Werte.

Ich meine, sollte ein PC Flieskommazahlen nicht langsammer berechnen können als Integerzahlen?

Grüße,
Ryo

bei Antwort benachrichtigen
Andreas42 RyoOhki „Zeit für Integer/Floatrechnungen“
Optionen

Hi!

Was die Syntax angeht, kann ich dir nicht helfen - ich arbeite nicht mit VC++.

Das Füllen der Variablen mit Zufallswerten sollte klappen. (Lokale Veriablen vorher mit Konstanten füllen, wird der Compiler warscheinlich auch wegoptimieren.)

Ich würde den Benchmark unter reinem DOS laufen lassen (kein Witz). Da kann dann wenigstens keine andere Betriebssystemfunktion in die Messung quatschen... ;-)

Bis denn
Andreas

Hier steht was ueber mein altes Hard- und Softwaregedoens.
bei Antwort benachrichtigen
Borlander Andreas42 „Hi! Was die Syntax angeht, kann ich dir nicht helfen - ich arbeite nicht mit VC...“
Optionen
Ich würde den Benchmark unter reinem DOS laufen lassen
Da kann ich Dir nur beipflichten!

Unter Windows gibt es einfach zu viele Störfaktoren, die das Ergebnis beeinflussen können. Das fängt schon mit evtl. Mausbewegungen an, nebenbei laufen auch immer noch Prozesse im Hintergrund ab, die "wilkürlich" Prozessorleistung benötigen.

CU Borlander
bei Antwort benachrichtigen
Andreas42 RyoOhki „Zeit für Integer/Floatrechnungen“
Optionen

Hi!

Beachte bitte, das Compiler heute vor allem durch Code-Optimierungsfähigkeiten verbessert werden.

Wenn du wirklich zwei Interger- bzw- Real-Konstanten addiert hast, dann hat das der Compiler warscheinlich schon zur Übersetzungszeit ausgewertet und daraus eine einfache Zuweisung der berechneten Konstannte erzeugt:

Aus: add = 0,1 + 0,1

Wurde dann: Add = 0,2

Bis denn
Andreas

Hier steht was ueber mein altes Hard- und Softwaregedoens.
bei Antwort benachrichtigen
RyoOhki Andreas42 „Hi! Beachte bitte, das Compiler heute vor allem durch...“
Optionen

Das heist, es sollte gehen, wenn ich variabeln addieren und diese zu Programmbeginn mit zufälligen Zahlen fülle, oder?

Ja, das es am Compiler liegt habe ich auch shcon vermutet, war mir aber nicht sicher.

Kannst du mir vielleicht einen kurzen überblick geben, wie ich einen Weiteren Thread starten kann, so das ich zwei Schleifen parallel ausführen kann? Ichh abe leider keine echte C++ - Doku, nur dass, was MS mitliefert.

Vielen Dank!

Grüße, Ryo

bei Antwort benachrichtigen
Anonym Andreas42 „Hi! Beachte bitte, das Compiler heute vor allem durch...“
Optionen

Da liegen wir ja wohl ein wenig daneben. Nach der Compilierung und der Erzeugung eines lauffähigen Programms ist der Eingabewert völlig
bedeutungslos. Es wird immer der gleiche Speicherplatzbedarf zur Verfügung gestellt.Das Programm wird ja schließlich nicht bei jeder Eingabe neu compiliert.Ich glaube kaum, dass er einen Compiler gebaut,
ggf. bei der Syntaxanalyse die Werte def.und..........Eine Code-Optimierung greift jedenfalls bereits bei der Entwicklung des Compilers.

bei Antwort benachrichtigen
RyoOhki Anonym „Da liegen wir ja wohl ein wenig daneben. Nach der Compilierung und der Erzeugung...“
Optionen

ich denke was er meint ist folgendes:

wenn ich eine reihe Rechnungen wie

Add = 1 + 2
usw

oder

Add = 0,1 + 0,2
usw

ausführe, fast der Compiler das während des Komplierens direkt zu

Add = 3

oder
Add = 0,3

zusammen

so das der eigentliche Rechenschritt (1 + 2 oder 0,1 + 0,2) garnichtmehr vom Program ausgeführt werden muss.

verwende ich jetzt aber additionen wie

add = z + 1 , wobei z eine Zufalszahl ist, die bei Programmstart gezogen wird, kann der compiler diesen schritt nicht vereinfachen und die berechnung muss ja vom programm selbst ausgeführt wird, da der wert der zufallszahl ja erst bei programmstart zur verfügung steht.

ich hoffe, ich liege da jetzt nicht völlig falsch, das ganze ist ziemliches neuland für mich

grüße, ryo



bei Antwort benachrichtigen
Anonym RyoOhki „ich denke was er meint ist folgendes: wenn ich eine reihe Rechnungen wie Add 1 2...“
Optionen

Du mußt einmal unterscheiden zwischen dem Bau eines Compilers und dem
Entwurf eines Programms. Dazwischen liegen Welten. Du willst ein geschreibenes Programm compilieren. Das hat aber auch gar nichts mit dem Entwurf eines Compilers zu tun !!!

bei Antwort benachrichtigen
RyoOhki Anonym „Du mußt einmal unterscheiden zwischen dem Bau eines Compilers und dem Entwurf...“
Optionen

das habe ich doch auch garnicht gesagt ^-^'

nochmal:

andreas meinte:
wenn ich additionen wie "add = 1 + 1" in meinen queltext einfüge, macht der compiler daraus wärend des compilierens ganz schlicht
Add = 2. Demnach würde dann im eigentlichen programm Add = 2 und nicht Add = 1 + 1 ausgeführt.
deswegen wäre mein (im quellcode) add = 1 + 1
eigentlich garkeine addition mehr wenn ich das programm ausführe, da das programmdanneinfach add = 2 abhandelt.

hm, reden wir aneinander vorbei oder hab ich was falsch verstanden?

güße, ryo

bei Antwort benachrichtigen
Andreas42 RyoOhki „das habe ich doch auch garnicht gesagt - nochmal: andreas meinte: wenn ich...“
Optionen

Hi!

Was mich betrifft, so hast du mich richtig verstanden.

Nur so als Ergänzung:
Diesen "Umbau" des Quelltextes durch den Compiler macht schon mein alter Borland-Pascal-Compiler (ist dort auch im Handbuch beschrieben). Ich denke daher, dass dies eigentlich heute alle Compiler können (und einsetzen werden).

Bis denn
Andreas

Hier steht was ueber mein altes Hard- und Softwaregedoens.
bei Antwort benachrichtigen
Plazebo RyoOhki „Zeit für Integer/Floatrechnungen“
Optionen

hmm, blöde Frage, aber wenn das Programm eine Zufallszahl auswerten soll, wird dann nicht das Ergebnis verfälscht, da das Programm selbst für das zufällige Berechnen der Zahl eine gewisse Zeit benötigt?

bei Antwort benachrichtigen
RyoOhki Plazebo „hmm, blöde Frage, aber wenn das Programm eine Zufallszahl auswerten soll, wird...“
Optionen

wenn du die zufallszahl ziehen lässt bevor du die Zeitmessung beginnst, beeinflusst es die Zeitmessung nicht.




bei Antwort benachrichtigen
Anonym RyoOhki „Zeit für Integer/Floatrechnungen“
Optionen

Assembler und C unterscheiden sich weniger als man denkt. Daher sollte man auch von dieser Seite das Problem betrachten. Codeoptimierung ist schon einmal etwas anderes. Ich habe schon Compiler geschrieben und kenne daher die Richtung der Optimierung. Damit jedenfalls läßt sich die Compilierungszeit nicht nenneswert beeinflussen.Und das ist ja nun der Sinn der Optimierung. Nun betrachte das Problem einmal anders: int werden über den stack übergeben und belegen ein Wort. float, double, long double werden über den Coprozessor ebenfalls über den stack übergeben und belegen ebenfalls ein Wort.Wo soll es denn nun zu einer meßbaren Verzögerung kommen, wenn der Proz. in beiden Fällen zwecks weiterer Operationen auf den stack zugreift???

bei Antwort benachrichtigen
RyoOhki Anonym „Assembler und C unterscheiden sich weniger als man denkt. Daher sollte man auch...“
Optionen

hmmmm, naja, ich bin mir nicht sicher, es ist schon lange her, aber soweit ich weis, werden Integerzahlen in einem block übermittelt, fließkommazahlen jedoch in zwei schriten
einmal ein vorzeichen (matise oder so) und einmal der exponent
so das die tatsächliche kommazahl durch diese beiden errechnet wird. und das deswegen das arbeiten mit fließkommarechnungen länger dauert als mit integerwerten.
ja, so oder ähnlich glabe ich. bitte korrigier mich fals es falsch ist.
weist du, ob die deklaration float eine fließkomma oder eine festkomma-zahl deklariert?

bei Antwort benachrichtigen
Anonym RyoOhki „hmmmm, naja, ich bin mir nicht sicher, es ist schon lange her, aber soweit ich...“
Optionen

Das Ganze erfolgt in getrennten Schritten. Float-Berechnungen sind im Coproz. optimiert. Coproz.: Zusätzlicher Prozessor, der für ganz bestimmte Berechnungen optimiert wurde. Bei älteren PCs mit Prozessoren der 486er Klasse und davor gab es oft einen eigenen Prozessor, der nur für Fließpunktberechnungen zuständig war (mathematischer Coprozessor). Bei heutigen Prozessoren sind die Funktionen dieses Coprozessors bereits integriert. Wie gesagt mußt du die Operationen getrennt betrachten. Evtl. Unterschiede bei der stackablage wirst du mit deiner Uhr wohl kaum messen können (mal in Kürze gesagt).

bei Antwort benachrichtigen
RyoOhki Anonym „Das Ganze erfolgt in getrennten Schritten. Float-Berechnungen sind im Coproz....“
Optionen

ja, so in etwa hatte ich mir das vorgestellt, aber was ist wemnn ich die zeit für mehrere miliarden berechnungen stoppe?

Grüße, Ryo

bei Antwort benachrichtigen
Anonym RyoOhki „ja, so in etwa hatte ich mir das vorgestellt, aber was ist wemnn ich die zeit...“
Optionen

Mach doch einmal !!! Und schick mir das Ergebnis !! Was erwartest du denn ?? Ich glaube, du siehst immer noch nicht wo die einzelnen Prozesse ablaufen.

bei Antwort benachrichtigen
RyoOhki Anonym „Mach doch einmal !!! Und schick mir das Ergebnis !! Was erwartest du denn ?? Ich...“
Optionen

kann sein
warscheinlich ;-)

ich werde mich mal näher damit beschäftigen

grüße und dank,
ryo

bei Antwort benachrichtigen
Borlander Anonym „Das Ganze erfolgt in getrennten Schritten. Float-Berechnungen sind im Coproz....“
Optionen

An den Co-Prozessor musste ich auch gleich denken, als ich das Posting gelesen habe. Wenn der eine Fließkommaopperation mit genausovielen Taktschritten abarbeitet wie der "Normale" Prozessor eine Ganzzahloperation (müsste durch paralleles Ausführeren mehrerer Operationen, Ganzzahlanteil / Exponent , für eine Fließkommarechnung möglich sein) sollte es eigentlich keinen Geschwindigkeitsunterschied geben.

CU Borlander

bei Antwort benachrichtigen
Dreamforger RyoOhki „Zeit für Integer/Floatrechnungen“
Optionen

Normalerweise sind doch die variablen die du addieren willst im Arbeitsspeicher. D.h für die Addition ist auch ein Bustransfer nötig.
Wenn du jetzt zwei schleifen vom Typ

for (i = 0; i

durchführst sollte jeder Compiler in der Lage sein x über mehrere Iterationen in Registern zu halten.

Ich würde mal vorschlagen das du besser die Werte in großen Arrays addierst, damit du mehr Speicherzugriffe bekommst. Normalerweise werden deine Werte ja auch erst aus dem Speicher gelesen

bei Antwort benachrichtigen
label Dreamforger „Cache /Pipelineing /Register“
Optionen

Blödsinn! Der Compiler optimiert in der Regel einfache Schleifen einfach weg. Ein Beispiel:

Source:

int main (void)
{
double a = 0.1, b = 0.1, sum = 0;
int i = 0;

for (i=0; i {
sum = a + b;
}

return;
}

--------------------------------
(gcc ohne Optimierungen)

main:
pushl %ebp
movl %esp,%ebp
subl $28,%esp
movl $-1717986918,-8(%ebp)
movl $1069128089,-4(%ebp)
movl $-1717986918,-16(%ebp)
movl $1069128089,-12(%ebp)
movl $0,-24(%ebp)
movl $0,-20(%ebp)
movl $0,-28(%ebp)
movl $0,-28(%ebp)
.p2align 4,,7
.L2:
cmpl $40000000,-28(%ebp)
jle .L5
jmp .L3
.p2align 4,,7
.L5:
fldl -8(%ebp)
faddl -16(%ebp)
fstpl -24(%ebp)
.L4:
incl -28(%ebp)
jmp .L2
.p2align 4,,7
.L3:
jmp .L1
.p2align 4,,7
.L1:
leave
ret

--------------------------------
(gcc mit -O2)

main:
pushl %ebp
movl %esp,%ebp
movl $40000000,%eax
.p2align 4,,7
.L5:
decl %eax
jns .L5
leave
ret

Da bleibt einfach nichts mehr von der Addition der Floats übrig.

bei Antwort benachrichtigen
Dreamforger label „Cache /Pipelineing /Register“
Optionen

Ok Klugscheisser.
Assembler code posten und Assembler code lesen und verstehen können sind zwei Dinge.
Beim nicht optimierten Code wird a noch aufaddiert:

.L2:
-- Schleifenbedingung
cmpl $40000000,-28(%ebp)
-- einsprung in die Schleife
jle .L5
-- aussprung aus der Schleife
jmp .L3
.p2align 4,,7
.L5:
-- hier kommt der eigentliche Schleifeninhalt
fldl -8(%ebp)
faddl -16(%ebp)
fstpl -24(%ebp)
.L4:
-- i++
incl -28(%ebp)
-- zur bedingung zurückspringen
jmp .L2

Beim optimierten Code wurde die Addition weggelassen weil auf das Ergebniss nicht mehr zugegriffen wird. Setz dahinter mal ein printf(...) und schau's dir nochmal an. Die Schleife ist nochimmer da, wenn auch effizienter:

-- schleifenzähler initialisieren
movl $40000000,%eax
.p2align 4,,7
.L5:
-- schleifenrelevante berechnungen
-- hier halt keine

-- schleifenzähler dekrement
decl %eax
-- rücksprung in die schleife
jns .L5

Wie soll der Compiler auch Schleifen entfernen? Dafür wäre semantisches Verständniss nötig. Und das fehlt dem Compiler. Alles was ein Compiler machen darf sind syntaktische äquivalente einzusetzen. Vieleicht greift ja ein anderer Thread auf die Variablen zu? -->volatile.
Die schleife, so wie ich sie geschrieben habe ist natürlich optimierbar. --> kleiner Gauss
Hierfür ist aber ein gewisses Mathematisches Wissen erforderlich. Der Compiler könnte auf dem selben Weg auch noch geometrische Reihen entfernen. Er könnte doch auch noch Taylorreihenentwicklungen erkennen und sie durch ihre Äquivalenten Formel ersetzen. Wann ist denn schluss mit solchen Optimierungen? Numerische Integration? Differentialgleichungen?

Nenn meine Posts nicht ungerechtfertigt Blödsinn und du kannst solche Antworten auch in einem ganz anderen Ton haben.

bei Antwort benachrichtigen
RyoOhki Dreamforger „Cache /Pipelineing /Register“
Optionen

Hmm, mag ja stimmen, aber da es bei miram C++ harpert kann ich nicht wirklich viel mit Assembler-Codes anfangen (...)

Hast du vielleciht einen genauen Vorschlag, wie ich es anstellen kann?

ic hahbe es jetzt soweit, das mei programm auf 90000 integeradditionen und 70000 multiplikationen pro milisekunde kommt (einfache variabeln, keine arrays, 5000 additionen pro schleifendurchlauf, insgesammt 20 mliarden additionen/multilikationen

weis vielleciht jemand, ob man an 'echte' vergleichswerte kommt?

grüße, ryo

bei Antwort benachrichtigen
label RyoOhki „Cache /Pipelineing /Register“
Optionen

Du kannst es Dir selber ausrechnen. Die "Programming Manuals" von Intel bzw. AMD ziehen. Dort stehen die pro Befehl benötigten Taktzyklen drin.

Auch wenn Du nicht Assembler kannst, ist es relativ einfach:
1. Mache ein Assembleroutput (im Handbuch deines Compilers steht drin, wie man es macht, z.B. bei gcc: "gcc -S test.c" erzeugt ein Assembleroutput mit dem Namen "test.s")

2. Berechne die Schleifendauer: Addiere dazu die Anzahl der Taktzyklen (pro Befehl, aus dem Manual rauskramen) zusammen und teile es durch die Taktfrequenz. Die Zahl, die dabei herauskommt, ist in Sekunden.

Der 2. Punkt ist ein wenig mühsam und wenn man es genau machen will ziemlich zeitaufwändig, da je nach Prozessortyp die Angabe der Taktzyklen schwankt. Das hängt dabei von verschiedenen Faktoren wie etwa Adressierungsart, verwendete Register, Boundaries etc. Erschwerend wird es denn noch, wenn man Effekte wie Memory-Zugriff berücksichtigen muß, da der Hauptspeicher im Verhältnis zu den Registern unglaublich langsam ist (100MHz ergeben ca. 800 MB/s übetragungsrate vom RAM zur CPU). Naja, und dann gibt es noch den L1/L2-Cache, der die Geschichte absolut verkompliziert :-)

Wenn Du experimentell den Unterschied zwischen Floating-Point und Integer-Addition am besten messen willst, dann schreib ein Programm, daß ein großen Array mit Zufallswerten füllt (jeweils int bzw. double, am besten > 100.000 Werte, damit es keinen Cache-Effekt gibt) und addiere die Werte einmal zufällig und einmal sequentiell (der Reihe nach). So hast Du ein gutes Tool zur Messung der wahren FP bzw. Integerperfomance unabhängig von dem Caches.

Ansonsten kannst Du auch Dir mal Linpack anschauen, dass ist ein Realworld-Benchmark, der je nach Konfig 500x500 bzw. 1000x1000 Matrizen berechnet.

Bis denne,
label.

bei Antwort benachrichtigen
mr.escape label „Cache /Pipelineing /Register“
Optionen

>Wenn Du experimentell den Unterschied zwischen Floating-Point und Integer-Addition am besten messen willst, dann schreib ein Programm, daß ein großen Array mit Zufallswerten füllt (jeweils int bzw. double, am besten > 100.000 Werte, damit es keinen Cache-Effekt gibt) und addiere die Werte einmal zufällig und einmal sequentiell (der Reihe nach). So hast Du ein gutes Tool zur Messung der wahren FP bzw. Integerperfomance unabhängig von dem Caches Ähh ... doch wohl eher einen test für die speicherzugriffsgeschwindigkeit. Bei so einer aktion wird kaum gerechnet, sondern auf den speicher gewartet!

mr.escape

"The man who trades freedom for security does not deserve nor will he ever receive either." - Benjamin Franklin"Wer seine Freiheit aufgibt, um Sicherheit zu erreichen, wird beides verlieren." - Georg Christoph Lichtenberg
bei Antwort benachrichtigen
Dreamforger mr.escape „Cache /Pipelineing /Register“
Optionen

Ich gehe mal davon aus das der Thread mit dem Hintergedanken begann welcher Datentyp effizienter in einem Programm verwendet werden kann.

Und dabei wird es auch zu Speicherzugriffen kommen.

bei Antwort benachrichtigen
mr.escape Dreamforger „Cache /Pipelineing /Register“
Optionen

Vollkommen richtig, aber die sollten aus dem 1st level cache kommen um die berechnung nicht zu beeinflussen. Die vielfältigen mechanismen einer modernen cpu (register renaming, speculative execution, out of order execution, jump prediction etc.), um zeit zu sparen machen richtige benchmarks sowieso fast unmöglich, da man jeweils nur sagen kann, dass eine bestimmte anwendung auf einer bestimmten cpu etwa die und die geschwindigkeit erreicht. Eine inhaltlich identische anwendung, lediglich in einer anderen sprache geschrieben oder gar nur mit einem anderen compiler übersetzt bringt wieder ganz andere ergebnisse. Selbst der mehrfache start kann aufgrung von cachezuständen deutlich unterschiede hervorbringen. Mal liegt ein allokierter speicherbereich cachegünstig, dann mal nicht. Am besten also beide varianten implementieren und testen. Grosse unterschieden werden aber wohl eh nicht rauskommen, da zuviele andere arbeitsschritte interferieren und die fraglichen befehle unter günstigen umständen in einem takt (manchmal auch mehrere parallel) abgearbeitet werden können.

mr.escape

"The man who trades freedom for security does not deserve nor will he ever receive either." - Benjamin Franklin"Wer seine Freiheit aufgibt, um Sicherheit zu erreichen, wird beides verlieren." - Georg Christoph Lichtenberg
bei Antwort benachrichtigen
label Dreamforger „Cache /Pipelineing /Register“
Optionen

Mann, sei doch nicht gleich so empfindlich. Aber was Du vorher geschrieben hast, war eben nicht korrekt. Und ja, ich habe mich auch nicht korrekt ausgedrückt, da ich mit "Schleife entfernen" die fehlende Float-Berechnung gemeint habe.

Aber Du hast hier definitiv ein Problem der Optimierungen angesprochen, deshalb verwendet man in der wissenschaftlichen Welt auch noch immer Fortran, da die Optimierungen in Bibliotheken geschieht (gibt sehr gute z.B. für Matrizenrechnungen), die aber verifiziert wurden. C/C++ ist dafür Käse, zumal es eher kryptisch wirkt und Fortran eher wie BASIC leicht lesbar ist.

Bis denne,
label.

bei Antwort benachrichtigen
Dreamforger label „Cache /Pipelineing /Register“
Optionen

Tut mir auch leid, ich habe überreagiert.

Und das mit der wissenschaftlichen Welt möchte ich nicht gerne so stehen lassen. Fortran ist auch einem sehr absteigenden Ast. In meiner Firma wurde inrgendwann mal ein Fortran zu C Converter verwendet. Seither wird in C weiterentwickelt. Fortran war für das Projekt nicht mehr haltbar.

bei Antwort benachrichtigen