Forum: PC-Programmierung Laufzeit einer Parameterübergabe in C++


von cFighter (Gast)


Lesenswert?

Hallo,

ich habe eine Frage an euch...

Folgendes Problem:

Bei der Laufzeitanalyse einer Parameterübergabe (lesen und schreiben) 
habe ich in einem kleinen C++ Testprogramm Laufzeiten ermittelt 
(Timerauflösung bei ca. 0,580 ns). Unterschieden wird zwischen 
Referenzen, Values und Pointern. Es wurde für jede dieser 
Übergabemöglichkeiten in einer Schleife (je 5.000.000 Durchläufe) einem 
Objekt ein anderes Objekt hinzugefügt und wieder ausgelesen. Die 
Laufzeiten der Parameterübergabe und den Methodenaufrufen werden 
anschließend noch gemittelt.

Nur zum besseren Verständnis erst mal der Quellcode:
1
int main(int argc, char* argv[])
2
{
3
  Timer t1;
4
  Timer t2;
5
  Timer t3;
6
7
  double duration1 = 0.0;
8
  double duration2 = 0.0;
9
  double duration3 = 0.0;
10
11
  Object o1;
12
  Object o2;
13
  Object o3;
14
15
  for(int i = 0; i < CALLS; i++)
16
  {
17
    Object2 o;
18
19
    t1.start();
20
21
    o1.setParameter(o);
22
    o = o1.getParameter();
23
24
    t1.stop();
25
    duration1 += t1.getDurationInSecs();
26
  }
27
28
  cout << "Referenz: " << duration1/CALLS << endl;
29
30
  for(int i = 0; i < CALLS; i++)
31
  {
32
    Object2 o;
33
34
    t2.start();
35
    
36
    o2.setRefParameter(o);
37
    o2.getRefParameter(o);
38
    
39
    t2.stop();
40
    duration2 += t2.getDurationInSecs();
41
  }
42
43
  cout << "Value: " << duration2/CALLS << endl;
44
45
  for(int i = 0; i < CALLS; i++)
46
  {
47
    Object2 *o = new Object2();
48
49
    t3.start();
50
    
51
    o2.setPointerParameter(o);
52
    o = o2.getPointerParameter();
53
    
54
    t3.stop();
55
    duration3 += t3.getDurationInSecs();
56
57
    delete o;
58
  }
59
60
  cout << "Pointer: " << duration3/CALLS << endl;
61
62
  Sleep(200000);
63
64
  return 0;
65
}

Das Objekt "Object" enthält lediglich ein Stack- und ein Heap-Objekt von 
"Object2". Das "Object2" enthält keinerlei Daten.

Die Laufzeiten betragen.

Referenz: 514,8 ns
Pointer:  543 ns
Value:    514,5 ns

Nun zu meiner eigentlichen Frage:

Wieso ist die Laufzeit bei der Übergabe eines Pointers so hoch. Ich 
hätte erwartet, dass die Laufzeiten in der Reihenfolge Value > Pointer > 
Referenz auftreten.

Grüße,
cFighter

von (prx) A. K. (prx)


Lesenswert?

Mach es kompilierbar oder schau in den erzeugten Code. Auf C Ebene ist 
das Kaffeesatzlesen.

von Peter II (Gast)


Lesenswert?

vermutlich ist das Problem das anlegen des objects.

> Object2 *o = new Object2();

hier muss der Heap genutzt werden, bei den anderen liegt das objekt auf 
dem Stack und immer an der gleichen stelle. Dies macht vermutlich ein 
grossteil der Laufzeit aus.

von Peter II (Gast)


Lesenswert?

nachtrag:

die Zeitmessung startet zwar danach, aber ich würde der Reihenfolge 
nicht trauen.

von (prx) A. K. (prx)


Lesenswert?

Nicht nur das. In dieser Dimension darf man Effekte durch Caches aller 
Art nicht vernachlässigen. Daher sollte man mindestens den ersten 
Durchlauf eines Testcodes nicht mit in die Messung nehmen.

von Karl H. (kbuchegg)


Lesenswert?

> einem Objekt ein anderes Objekt hinzugefügt

Was heißt hinzugefügt?

Oder anders gefragt: wie sehen die einzelnen set und get Methoden aus?


Und ob das hier

    t3.start();

    o2.setPointerParameter(o);
    o = o2.getPointerParameter();

    t3.stop();

kummulativ eine halbwegs brauchbare Zeit ergibt, ist auch fraglich. 
Stell dir vor du stoppst mit einer Kirchturmuhr den Carl Lewis auf 
seinem 100 Meter Lauf. Da wird nichts vernünftiges rauskommen, egal wie 
oft du die Messung wiederholst und die Einzelzeiten addierst. Bei 98 von 
100 Versuchen wird die Zeit 0 sein und bei 2 dieser 100 Messungen hat 
sich der Stundenzeiger weiterbewegt: Summe 2 Stunde. Die 100 Versuche 
haben also 2 Stunde 'Zeit verbraucht'. Macht knapp 1.2 Minuten pro Lauf. 
Das schaff sogar ich auf 100 Meter. Und ich bin kein Olympiasieger.

von .... (Gast)


Lesenswert?

Entweder compilieren ohne Optimierungen und dann den Assemblercode 
analysieren. Oder an deinem ganz konkreten Programm die Messungen 
machen. Alles andere ist wirklich Kaffeesatzlesen. Da sind einfach zu 
viele Nebeneffekte dabei.

von cFighter (Gast)


Lesenswert?

Hi,

vielen Dank für eure Antworten. Die haben mir einige neue Erkenntnisse 
gebracht (nur leider war noch nicht die Lösung dabei die eine Erklärung 
dafür gibt). Nach einer neuen kleinen Messung mit dem Folgenden 
Quellcode
1
#include "stdafx.h"
2
3
int main(int argc, char* argv[])
4
{
5
  Timer t1;
6
  Timer t2;
7
  Timer t3;
8
9
  Object o1;
10
  Object o2;
11
  Object o3;
12
13
  Object2 oStack;
14
  Object2 *oHeap = new Object2();
15
16
  t1.start();
17
18
  for(int i = 0; i < CALLS; i++)
19
  {
20
    o1.setParameter(oStack);
21
    oStack = o1.getParameter();
22
  }
23
24
  t1.stop();
25
  cout << "Referenz: " << t1.getDurationInSecs()/CALLS << endl;
26
27
  t2.start();
28
29
  for(int i = 0; i < CALLS; i++)
30
  {
31
    o2.setRefParameter(oStack);
32
    o2.getRefParameter(oStack);
33
  }
34
35
  t2.stop();
36
  cout << "Value: " << t2.getDurationInSecs()/CALLS << endl;
37
38
  t3.start();
39
40
  for(int i = 0; i < CALLS; i++)
41
  {
42
    o2.setPointerParameter(oHeap);
43
    oHeap = o2.getPointerParameter();
44
  }
45
46
  t3.stop();
47
  cout << "Pointer: " << t3.getDurationInSecs()/CALLS << endl;
48
49
  delete oHeap;
50
51
  Sleep(200000);
52
53
  return 0;
54
}

kamen folgende Laufzeiten heraus:

Referenz: 5,02857e-013 s
Value:    5,02857e-013 s
Pointer:  1,27072e-009 s

Leider sieht es immer noch ähnlich aus.

Meine Setter und Getter sehen folgendermaßen aus:
1
void Object::setRefParameter(Object2 &o)
2
{
3
  this->o = o;
4
}
5
6
void Object::getRefParameter(Object2 &o)
7
{
8
  o = this->o;
9
}
10
11
void Object::setParameter(Object2 o)
12
{
13
  this->o = o;
14
}
15
16
Object2 Object::getParameter()
17
{
18
  return o;
19
}
20
21
void Object::setPointerParameter(Object2 *o)
22
{
23
  this->oPointer = o;
24
}
25
26
Object2* Object::getPointerParameter()
27
{
28
  return oPointer;
29
}

Wenn ihr noch weitere Tipps oder Erklärungen dafür habt wäre ich dankbar 
;-)...

Vielen Dank!

von (prx) A. K. (prx)


Lesenswert?

cFighter schrieb:

> Referenz: 5,02857e-013 s

Das sind 0.5 Pikosekunden pro Iteration. Respekt. Wenn der Prozessor 
also mindestens einen Takt pro Iteration benötigt, dann wird er mit 
mindestens 2000 Ghz getaktet.

von Peter II (Gast)


Lesenswert?

dir ist bewust das hier eine kopie von dem objekt gemacht wird, dies 
kostet zeit!

void Object::setRefParameter(Object2 &o)
{
  this->o = o;
}


wie ist du die zeitmessung implementiert? Sicher das du durch das double 
nicht mehr fehler reinrechnest? Sotewas macht man lieber mit Bigint in 
nanosekunden. (je nach Plaftform)

Mach doch mal bitte ein quellcode fertig wo alles drin steht, damit 
können wir uns dann selber davon überzeugen.

von Karl H. (kbuchegg)


Lesenswert?

cFighter schrieb:

> Wenn ihr noch weitere Tipps oder Erklärungen dafür habt wäre ich dankbar
> ;-)...

<Durch die Zähne pfeif>

Der Optimizer ist besser als ich dachte :-)

von (prx) A. K. (prx)


Lesenswert?

Peter II schrieb:

> dir ist bewust das hier eine kopie von dem objekt gemacht wird, dies
> kostet zeit!

Bei einem leeren Objekt hält sich der Aufwand dafür in Grenzen.

Sein Problem bei der letzten Messung könnte massgeblich damit zusammen 
hängen, dass der Optimizer klüger ist als der Programmier.

von cFighter (Gast)


Lesenswert?

Das ist mit ziemlicher Wahrscheinlichkeit die Timerauflösung die da 
zubuche schlägt! Aber leider ist die Parameterübergabe mittels Pointer 
immer noch Langsamer als die mit Value!

Ich werde mal versuchen den Assemblercode zu verstehen! melde mich die 
Tage nocheinmal (vielleicht auch erst nächste Woche --> cFighter geht 
jetzt ins lange Wochenende!)

Euch natürlich erst mal vielen Dank und auch ein schönes WE!

von Karl H. (kbuchegg)


Lesenswert?

cFighter schrieb:
> Das ist mit ziemlicher Wahrscheinlichkeit die Timerauflösung die da
> zubuche schlägt! Aber leider ist die Parameterübergabe mittels Pointer
> immer noch Langsamer als die mit Value!

Geh mal davon aus, dass deine Messwerte nicht stimmt.
Davon jetzt irgendwas abzuleiten ist nicht angebracht.

Deine Devise mit diesem Zahlenwerk muss lauten:
Wer misst, misst viel Mist.


> Referenz: 5,02857e-013 s
> Value:    5,02857e-013 s
> Pointer:  1,27072e-009 s

Die e-009 könnten noch so einigermassen realistisch sein, obwohl mir der 
Wert auch noch um mindestens eine gute 10-er Potenz zu niedrig vorkommt. 
Aber die e-013 sind völlig unmöglich. Wie A.K. weiter oben schon 
schrieb: Selbst wenn wir alles zu Gunsten der CPU ins Feld führen und 
mehr oder weniger alles vernachlässigen, was da an Arbeit für den 
Rechner notwendig ist, müsste dein PC mit 2000 Ghz getaktet sein, damit 
ein Vorstoss in diesen Zeitbereich möglich wird. Und 2000 Ghz hast du 
ganz sicher nicht.

Der Plausibilitätscheck sagt: Die Zahlen sind gewürfelt aber auf keinen 
Fall real.

von arno (Gast)


Lesenswert?

hmm 2000 GHz ? oder.......... vielleicht n GHz die operation nur einmal 
in n Schritten ausgeführt statt 2000mal?

von (prx) A. K. (prx)


Lesenswert?

Eben. Stichwort: Optimizer. Siehe auch die bei Anfängern beliebten aber 
vom Compiler wegoptimierten Zeitschleifen.

von Wurstbrot (Gast)


Lesenswert?

Wie sieht denn der Timer Code aus?

von Karl H. (kbuchegg)


Lesenswert?

Es geht mehr ums Symptom an sich.
Bei hinreichend komplexem Code macht es speziell in C++ kaum mehr Sinn 
Einzelaktionen mittels Timer auszumessen. Gerade C++ holt sich sehr viel 
Speed aus Compileroptimierungen. Timing-Messungen machen da auf 
algorithmischer Ebene Sinn. Einzeldinge auszumessen (wie lange dauert 
ein for, wie lange der sinngleiche while, was ist schneller Pass per 
Reference/Pass per Pointer macht meistens keinen Sinn. Wer denkt, er 
könne so sein Programm wesentlich 'optimieren' hat meistens mit Zitronen 
gehandelt. Überlass solche Dinge dem Compiler, der macht das 
zuverlässig. Schaff ihm die Möglichkeit zum Optimieren (kleine 
Funktionen inline in die Klassendefinition) und ansonsten kümmere dich 
um die Algorithmen. Da ist mehr zu holen.

Mit einem Entscheidungsbaum

+ muss die Funktion das Argument beim Aufrufer ändern können?
|
+-- Ja:
|     benutze eine Referenz
|
+-- Nein: muss der Aufrufer mitteilen können:
    |     Ich hab nichts für dich?
    |
    +-- Ja: benutze einen Pointer, wobei NULL genau diese Zusatzinfo
    |       darstellt.
    |
    +-- Nein: Handelt es sich um einen primitiven, eingebauten
        |     Datentyp?
        |
        +-- Ja: benutze pass per Object Copy
        |
        +-- Nein: muss die Funktion sowieso eine Kopie machen?
            |
            +-- Ja: benutze pass per Object Copy
            |
            +-- Nein: benutze eine const Referenz

(bzw. sinngemäss die Fälle, die ich hier vergessen habe ... Grundprinzip 
ist: Pass per Referenz, es sei denn spezielle Fälle liegen vor)

Mit diesen Grundentscheidundungen kriegst du Parameterpassing, an dem du 
nicht mehr viel drehen musst. Langsame Programme sind nicht deswegen 
langsam, weil Pass per Pointer schneller/langsamer ist als Pass per 
Reference.

von cFighter (Gast)


Lesenswert?

Hallo,

entschuldigt meine lange Abwesenheit. Ich habe spontan noch eine andere, 
dringende Aufgabe bekommen.

Nun zu meiner Messung:

A. K. schrieb:
> cFighter schrieb:
>
>> Referenz: 5,02857e-013 s
>
> Das sind 0.5 Pikosekunden pro Iteration. Respekt. Wenn der Prozessor
> also mindestens einen Takt pro Iteration benötigt, dann wird er mit
> mindestens 2000 Ghz getaktet.

Hierbei beziehen sich die 5 Pikosekunden auf EINE Iteration! Ich messe 
zuerst die Gesamtdauer aller Iterationen und teile anschließend durch 
die Iterationsanzahl (im Quellcode CALLS = 5.000.000). Somit wird hier 
lediglich das Mittel eines sets und eines gets bestimmt.

Vielleicht habe ich euch da ja auch falsch verstanden, dann bitte 
nochmal klarstellen.

kbuchegg hat im letzten Beitrag geschrieben, dass es keinen Sinn macht 
die Parameterübergabe an Funktionen zu optimieren. Das sehe ich nicht 
ganz so.
Da dieses Projekt mein erstes C++ Projekt ist habe ich zu beginn eine 
relativ hohe Laufzeit von ca. 200ms gehabt. Die Compileroptimierung hat 
da noch ca. 50% heraus holen können. Jetzt, nachdem ich temporäre 
Objekte und Parameterübergaben geändert bzw. entsorgt habe, habe ich 
eine Laufzeit von ca 5ms!!! Die temporären Objekte waren hierbei im 
Zusammenhang mit Pointern, welche an verschiedene Methoden übergeben 
wurden.
Nun wollte ich aufgrund des krassen Unterschiedes (200ms zu 5ms) die 
Laufzeiteigenschaften der Parameterübergabe untersuchen. Ich hätte nie 
gedacht das das soooo viel aufwand ist.

Den Assemblercode habe ich mir bisher noch nicht angesehen. Aber 
vielleicht begründe ich das Nutzen der Referenzen einfach damit, dass 
weniger temporäre Objekte genutzt werden wodurch die Laufzeit verbessert 
wurde!

Wenn ihr noch eine einfache und bessere Idee habt könnt ihr gerne 
nochmal euren Senf dazu geben ;-)

Vielen Dank euch erst mal...

von Karl H. (kbuchegg)


Lesenswert?

cFighter schrieb:

>> Das sind 0.5 Pikosekunden pro Iteration. Respekt. Wenn der Prozessor
>> also mindestens einen Takt pro Iteration benötigt, dann wird er mit
>> mindestens 2000 Ghz getaktet.
>
> Hierbei beziehen sich die 5 Pikosekunden auf EINE Iteration! Ich messe
> zuerst die Gesamtdauer aller Iterationen und teile anschließend durch
> die Iterationsanzahl (im Quellcode CALLS = 5.000.000). Somit wird hier
> lediglich das Mittel eines sets und eines gets bestimmt.
>
> Vielleicht habe ich euch da ja auch falsch verstanden, dann bitte
> nochmal klarstellen.

Dir scheint nicht klar zu sein, worauf das hinausläuft.
Egal ob MIttelwert oder nicht. Rechnet man zurück, wie schnell dein 
Prozessor sein müsste um EINE derartige Instruktion zu machen, dann 
kommt kman drauf, dass der Prozessor ca 2000 GIGA-HERZ machen müsste. 
Was selbstverständlich kompletter Unsinn ist, denn KEIN Prozessor macht 
zur Zeit 2-tausend-Giga-Herz. Wir stehen erst technologisch bei ca 3 
(drei). 2-tausend zu drei ist aber ein krasser Unterschied. Die einzige 
mögliche Erklärung dafür lautet: Deine Messung ist um einen Faktor von 
rund 1000 falsch und somit unbrauchbar!

Wer misst, misst viel Mist.

> kbuchegg hat im letzten Beitrag geschrieben, dass es keinen Sinn macht
> die Parameterübergabe an Funktionen zu optimieren.

Nun ja. Die der jeweilgen Situation angemessene Übergabemethode zu 
benutzen würde ich noch nicht als Optimierung bezeichnen. Du wirst ja 
schliesslich das Berechnen der Fläche eines Rechtecks (a: 8 Meter, b: 7 
Meter) in der Form

    8 * 7

und nicht mittels

    8 + 8 + 8 + 8 + 8 + 8 + 8

auch nicht als 'Optimierung' bezeichnen. Oder doch?

> Das sehe ich nicht
> ganz so.
> Da dieses Projekt mein erstes C++ Projekt ist habe ich zu beginn eine
> relativ hohe Laufzeit von ca. 200ms gehabt. Die Compileroptimierung hat
> da noch ca. 50% heraus holen können. Jetzt, nachdem ich temporäre
> Objekte und Parameterübergaben geändert bzw. entsorgt habe, habe ich
> eine Laufzeit von ca 5ms!!! Die temporären Objekte waren hierbei im
> Zusammenhang mit Pointern, welche an verschiedene Methoden übergeben
> wurden.

Das du wissen musst, was du tust, habe ich erst mal vorausgesetzt.
Wenn du natürlich haufenweise temporäre Objekte erzeugst, darfst du dich 
nicht wundern, wenn das langsam ist.
Die beste Optimierung ist immer noch, sein Werkzeug (in dem Fall die 
Sprache C++) zu beherrschen.

Und wenn du den Entscheidungsbaum ansiehst, wann welcher 
Übergabemechanismus angebracht ist, dann wirst du feststellen, dass Pass 
per Reference bevorzugt wird. Solange es keinen anderen Grund dagegen 
gibt, fährt man damit gut.

> Nun wollte ich aufgrund des krassen Unterschiedes (200ms zu 5ms) die
> Laufzeiteigenschaften der Parameterübergabe untersuchen. Ich hätte nie
> gedacht das das soooo viel aufwand ist.

Deine Zeiteinsparung kommt aber gar nicht aus dem Parameter Passing. 
Deine Codeeinsparung kommt aus der exzessiven Erzeugung von temporären 
Objekten. D.h. wenn du für die Zukunft etwas lernen willst, dann lerne 
wann, wo und wie du sinnlos temporäre Objekte erzeugt hast.


1
class MyClass
2
{
3
public:
4
  void doit() const;
5
  ...
6
};
7
8
void foo( MyClass a )
9
{
10
  a.doit();
11
}
12
13
int main()
14
{
15
  MyClass b;
16
17
  foo( b );
18
}

Q: Was sagt der Entscheidungsbaum von oben zu dieser Situation?
A: Er sagt: pass per const Reference.
Q: Was liegt tatsächlich vor?
A: Es liegt Pass per Copy vor.
Q: Ist das sinnvoll?
A: Nein, ist es nicht. Es gibt keinen Grund, warum in dieser
   Situation eine Objektkopie gemacht werden müsste. Daher ist
   Pass per Copy nicht sinnvoll. Pass per Reference hätte die Kopie
   vermieden.
Q: War daher das erzeugen eines 'temporären' Objektes sinnvoll?
A: Nein, war es nicht.
Q: Was könnte man tun?
A: Man könnte das machen, was sich aus dem Entscheidungsbaum bzw.
   aus der Daumenregel "Bevorzuge eine Referenz, wenn es keine Gründe
   dagegen gibt" ergibt und abändern
1
void foo( const MyClass& a )
2
{
3
 ...

OK, wenn du das als "Optimierung" ansehen willst, kannst du das 
natürlich gerne tun. Ich würde das aber eher in die Kategorie "sein 
Handwerkszeug richtig anwenden können" einordnen.

von cFighter (Gast)


Lesenswert?

Jetzt habe ich das mit den 2000 GHz verstanden!

Dann werde ich die Laufzeit (da sie sowieso mit Optimierung im 
Pikosekunden bereich liegt) vernachlässigen. Den Assemblercode habe ich 
mit gerade angesehen, wobei hier auch meine Vermutung der Laufzeit 
bestätig wird. (Value > Pointer > Referenz) --> sofern der 
Entscheidungsbaun erst mal egal ist.

Trotz dem euch allen vielen Dank für die Unterstützung

von Karl H. (kbuchegg)


Lesenswert?

cFighter schrieb:

> bestätig wird. (Value > Pointer > Referenz) --> sofern der
> Entscheidungsbaun erst mal egal ist.

Genau das ist aber der entscheidende Punkt:
Die Entscheidung ob Value, Pointer oder Referenz macht man davon 
abhängig, welches Verhalten die Funktionsschnittstelle implementieren 
muss und nicht davon, was davon schneller ist.

Denn wenn du eigentlich einen Pass per Value brauchen würdest, 
stattdessen aber aus Laufzeitgründen einen Pointer übergibst, dann ist 
zwar das Parameterpassing tatsächlich schneller. Allerdings wenn wir 
jetzt alles zusammenrechnen, bis du mit dem Pointer-Passing wieder 
funktional auf gleich mit dem Value-Passing bist, ist es in Summe 
langsamer.

Der Keller eines Hauses ist doppelt so schnell gemauert, wenn man nur 
jeden 2.ten Ziegelstein einbaut. Schon. Nur wenn dann letztendes das 
Dach aufgesetzt werden soll, sind umfangreiche Sanierungsmassnahmen 
notwendig, sonst stürzt der Keller ein.   -> In Summe ist man mit "jeden 
2.ten Ziegel weglassen" also nicht schneller, sondern langsamer. Selbst 
wenn die Maurer beim Keller-Mauern in 2 Tagen statt in 4 fertig waren.

von Klaus W. (mfgkw)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Die Entscheidung ob Value, Pointer oder Referenz macht man davon
> abhängig, welches Verhalten die Funktionsschnittstelle implementieren
> muss und nicht davon, was davon schneller ist.

Ja, soweit es um per value einerseits oder pointer/Referenz andererseits 
geht.

Das unterscheidet sich grundlegend funktional (und hinsichtlich 
Laufzeit).

Zeiger gegenüber Referenz ist dagegen i.d.R. nur noch Geschmacksfrage, 
weil in vielen Fällen beide gleichwertig sind (außer 1. wenn Zeiger 
nötig sind wg. C-Kompatibilität, oder 2. der Zeiger auf "ungültig", also 
NULL gesetzt werden können soll).
Wobei es natürlich besseren oder schlechteren Geschmak gibt...

Wenn hier für Zeiger gegenüber Referenz deutliche Laufzeitunterschiede 
gemessen werden, dann ist die Messung falsch.

von Vlad T. (vlad_tepesch)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Denn wenn du eigentlich einen Pass per Value brauchen würdest,
> stattdessen aber aus Laufzeitgründen einen Pointer übergibst, dann ist
> zwar das Parameterpassing tatsächlich schneller. Allerdings wenn wir
> jetzt alles zusammenrechnen, bis du mit dem Pointer-Passing wieder
> funktional auf gleich mit dem Value-Passing bist, ist es in Summe
> langsamer.

Hast du dafür Belege? Das halte ich für ein Gerücht.
warum soll die Übergabe von dem und Arbeit mit einem Pointer genauso 
langsam sein, wie das Kopieren von und Arbeiten mit dem Objektes.

Die Arbeit mit beidem innerhalb der Funktion sollte ziemlich gleich 
schnell gehen, da die Member des Objektes in beiden Fällen gleich 
adressiert werden, nur das bei dem Einen ein [Pointer + Memberoffset] 
und bei dem anderen ein [Stackbasispointer + Memberoffset] ausgelesen 
wird.

bleibt also noch der Kopiervorgang. und der ist langsamer sobald die 
Strukturgröße die Größe des Datentyps 'Pointer' überschreitet.

von Karl H. (kbuchegg)


Lesenswert?

Vlad Tepesch schrieb:
> Karl Heinz Buchegger schrieb:
>> Denn wenn du eigentlich einen Pass per Value brauchen würdest,
>> stattdessen aber aus Laufzeitgründen einen Pointer übergibst, dann ist
>> zwar das Parameterpassing tatsächlich schneller. Allerdings wenn wir
>> jetzt alles zusammenrechnen, bis du mit dem Pointer-Passing wieder
>> funktional auf gleich mit dem Value-Passing bist, ist es in Summe
>> langsamer.
>
> Hast du dafür Belege? Das halte ich für ein Gerücht.
1
void foo( const irgendwas * value )
2
{
3
  if( !value )
4
    return;
5
6
  irgendwas localCopy = *value;
7
8
  // arbeite mit localCopy
9
  localCopy.Member( 5 );   // Objekt ändern
10
}

versus
1
void foo( irgendwas localCopy )
2
{
3
   localCopy.Member( 5 );  // Objekt ändern
4
}


Das wird sich um nichts reißen.

Ich denke du hast diesen Satzteil
> Denn wenn du eigentlich einen Pass per Value brauchen würdest
überlesen oder falsch interpretiert oder ich hab mich schlecht 
ausgedrückt.
Die Funktion braucht eine lokale Kopie (aus welchen Gründen auch immer). 
Ob diese Kopie in der Funktion hergestellt wird oder beim Parameter 
Passing, ist laufzeitmässig egal. Bleibt noch der zusätzliche Pointer 
....

von Vlad T. (vlad_tepesch)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ich denke du hast diesen Satzteil
>> Denn wenn du eigentlich einen Pass per Value brauchen würdest
> überlesen oder falsch interpretiert oder ich hab mich schlecht
> ausgedrückt.

Ja ich glaub, ich hab dich misverstanden.

von cFighter (Gast)


Lesenswert?

Hallo,

ich melde mich auch nochmal zu Wort!

Meine Zeitmessung ist anfürsich richtig gewesen. Ich messe zwar jetzt 
die gesamte Schleife
1
  t1.start();
2
3
  for(int i = 0; i < CALLS; i++)
4
  {
5
    o1.setParameter(oStack);
6
    oStack = o1.getParameter();
7
  }
8
9
  t1.stop();

aber die Zeiten sind jetzt endlich interpretierbar!

Value:    0,210425 s
Referenz:  0,122376 s
Pointer:  0,141203 s

Das Problem worauf keiner von euch gekommen ist:

Die Messung meiner Zeiten beginnt mit dem Value-passing, jedoch gebe ich 
leider einen Text aus der die Zeiten als Referenz-Passing definiert! 
(sieht man oben im Code besser!)

Naja, trotz dem habe ich viele euren Kommentaren gelernt. Vielen Dank!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.