C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Permformance vs. Flexibilität

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ (German)
View previous topic :: View next topic  
Author Message
Christoph Rabel
Guest





PostPosted: Fri Nov 14, 2003 11:27 am    Post subject: Re: Permformance vs. Flexibilität Reply with quote



Andreas Nicolai wrote:
Quote:
double w = material.(*mrc)(pc); // sieht immer noch doof aus

------------------------------------------------

So, welche Variante ist wohl am schnellsten? Ich vermute ja mal Variante
3, aber die gefällt mir irgendwie nicht so recht (halt C-Style)...

Also wenn die Optik alles ist, was dich stört warum kapselst
du den Funktionspointeraufruf nicht in einer inline Funktion?

Damit schauts von aussen gut aus und Performancenachteile
solltest du auch keine haben.

Im Endeffekt kann ich dir aber nur empfehlen alle drei
Versionen zu nehmen und mit einem Profiler auszuprobieren.

mfg

Christoph

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de

Back to top
Stefan Reuther
Guest





PostPosted: Fri Nov 14, 2003 11:27 am    Post subject: Re: Permformance vs. Flexibilit344tn^ Reply with quote



Hallo,

Andreas Nicolai <Andreas.Nicolai (AT) web (DOT) de> wrote:
[Unterscheidung zwischen zwei Memberfunktionen]
Quote:
1. einen Wrapper schreiben in der Art:

double Material::mrc(double pc) const {
switch (function_type) {
case ANALYTICAL : return mrc_vG(pc);
case SPLINE : return mrc_spline(pc);
default : throw runtime_error("Invalid function type!"); //
oder so ähnlich
}
};

Hier hast du die Entscheidung nur versteckt, nicht entfernt. Das
ist langsam. Außerdem C-style :)


Quote:
2. Für beide Funktionen Klassen polymorphe Klassen schreiben und die
Entscheidung durch compilergenerierten Code durchführen.

// Rein abstrakte Basisklasse
class MaterialFunction : std::unary_function<double, double> {
public:
double operator()(double) const = 0;
};

Das ist die Lösung, die ich wählen würde.

Quote:
// Aufruf:
double w = material.(*mrc)(pc); // sieht doof aus

Und? Verpack's in eine inline-Funktion und gut ist.

Quote:
3. Funktionszeiger (*brrr*)

Anstelle eines Wrappers verwende ich einen Funktionszeiger auf die
jeweilige Memberfunktion

class Material {
public:
typedef (Material::*MatFunc)(double);

So, welche Variante ist wohl am schnellsten? Ich vermute ja mal Variante
3, aber die gefällt mir irgendwie nicht so recht (halt C-Style)...

C-Style? In C gibts keine Member-Funktions-Zeiger :)

Variante 2 dürfte aber immer noch schneller sein, insbesondere,
wenn es irgendwo virtuelle Funktionen gibt, die ebenfalls auf
die Signatur passen.

Außerdem ist Variante 2 flexibler. Ich weiß nicht, was dein
'Material' ist, aber falls irgendwann mal eine von 'Material'
abgeleitete Klasse hinzukommt, die eine weitere Berechnungs-
methode anbietet, ist das nur mit dieser Variante realisierbar.

Oder vielleicht möchtest du noch 'numerisch differenzieren' -
dann schreibst du eben eine Klasse
class NumDiff : public MaterialFunction {
MaterialFunction* base;
public:
NumDiff(MaterialFunction* b) : base(b) { }
double operator()(double d) const {
return ((*base)(d) - (*base)(d+1e-6)) / 1e-6;
}
};
Oder so.


Stefan

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de

Back to top
Markus Breuer
Guest





PostPosted: Fri Nov 14, 2003 11:33 am    Post subject: Re: Permformance vs. Flexibilität Reply with quote



Quote:
Dabei fallen mir jetzt 3 Lösungen ein und eine Frage: welche Variante
ist wohl die Schnellste?

1. einen Wrapper schreiben in der Art:

double Material::mrc(double pc) const {
switch (function_type) {
case ANALYTICAL : return mrc_vG(pc);
case SPLINE : return mrc_spline(pc);
default : throw runtime_error("Invalid function type!"); //
oder so ähnlich
}
};

Jeder Aufruf erfordert einen Vergleich mit function_type, bevor die
Zielfunktion aufgerufen werden kann. Da function_type innerhalb einer
Instanz nicht ständig ändern wird, sind Folgevergleiche meist unnötig.
Diese Variante ist die langsamste.

Quote:
// Aufruf
double w = material.mrc(pc);
------------------------------


2. Für beide Funktionen Klassen polymorphe Klassen schreiben und die
Entscheidung durch compilergenerierten Code durchführen.

// Rein abstrakte Basisklasse
class MaterialFunction : std::unary_function<double, double> {
public:
double operator()(double) const = 0;
};

// Jetzt die beiden Deklarationen der eigentlichen Funktionen
class MRC_vG : public MaterialFunction {
public:
double operator()(double pc) const;
};

class MRC_spline : public MaterialFunction {
public:
double operator()(double pc) const;
};

// Und die Klasse Material wird zu:
class Material {
public:
// ...
MaterialFunction* mrc;
// ...
};

// Aufruf:
double w = material.(*mrc)(pc); // sieht doof aus
Hier entscheidet jetzt der compilergenerierte Code anhand der virtuellen
Tabellen, welche Funktion genommen wird.

Mit einer kleinen Erweitung sieht das nicht mehr doof aus:

class Material {
public:
// ...
double mrc( double _pc ) { return this->*mrc(pc); )
// ...
};

double w = material.mrc( .... ); // sieht doch besser aus, gell!
Als inline-Funktion dürfte diese Variante genauso schnell sein, wie
oben. Spätestens mit dem Einschalten der Optimierungen ist das so.


Quote:
------------------------------


3. Funktionszeiger (*brrr*)

Anstelle eines Wrappers verwende ich einen Funktionszeiger auf die
jeweilige Memberfunktion

class Material {
public:
typedef (Material::*MatFunc)(double);
// ...
double mrc_vG(double pc) const; // berechnet den Wert
analytisch
double mrc_spline(double pc) const; // berechnet den Wert
via Spline-Interpolation
MatFunc mrc; // mrc = *mrc_vG or mrc =
*mrc_spline;
// ...
};

// Aufruf:
double w = material.(*mrc)(pc); // sieht immer noch doof aus

Diesen Aufruf kannst du wie unter 2. beschrieben, ebenfalls verschönern!

Quote:

------------------------------------------------

So, welche Variante ist wohl am schnellsten? Ich vermute ja mal Variante
3, aber die gefällt mir irgendwie nicht so recht (halt C-Style)...

Welche Variante würdet Ihr empfehlen?

2. und 3. liefern vergleichbare Ergebnisse. Du kannst dir eime Variante
aussuchen!

Gruß Markus

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de

Back to top
Rolf Magnus
Guest





PostPosted: Fri Nov 14, 2003 12:24 pm    Post subject: Re: Permformance vs. Flexibilität Reply with quote

Andreas Nicolai wrote:

Quote:
Hallo NG!

Habe eine Klasse Material, die verschiedene Materialfunktionen zur
Verfügung stellt. Diese können entweder analytische Funktionen oder
Splines sein:

class Material {
public:
// ...
double mrc_vG(double pc) const; // berechnet den Wert
analytisch
double mrc_spline(double pc) const; // berechnet den Wert
via
Spline-Interpolation
// ...
};

Was bedeutet 'mrc'?

Quote:
Welche der beiden Funktionen verwendet wird, ist erst zur Laufzeit
bekannt (z.B. durch eine Member-Variable 'function_type').
Jetzt müsste bei jeder Abfrage (und davon gibt es seeeeehr viele)
entschieden werden, welche der beiden Funktionen ich nun nehme.

Dabei fallen mir jetzt 3 Lösungen ein und eine Frage: welche Variante
ist wohl die Schnellste?

Ich kommentiere einfach mal, was ich erwarten würde. Ohne es zu testen,
kann man es aber nicht genau sagen. Es hängt schließlich vom Programm,
dem System, Compiler u.s.w. ab.

Quote:
1. einen Wrapper schreiben in der Art:

double Material::mrc(double pc) const {
switch (function_type) {
case ANALYTICAL : return mrc_vG(pc);
case SPLINE : return mrc_spline(pc);
default : throw runtime_error("Invalid function type!");
//
oder so ähnlich
}
};

Von der Lösung würde ich eine sehr gute Performance erwarten. Wenn die
Funktionen kurz sind, könnten sie sogar noch inline expandiert werden,
was noch weiter beschleunigen könnte.

Quote:
// Aufruf
double w = material.mrc(pc);
------------------------------


2. Für beide Funktionen Klassen polymorphe Klassen schreiben und die
Entscheidung durch compilergenerierten Code durchführen.

// Rein abstrakte Basisklasse
class MaterialFunction : std::unary_function<double, double> {
public:

virtual

Quote:
double operator()(double) const = 0;
};

// Jetzt die beiden Deklarationen der eigentlichen Funktionen
class MRC_vG : public MaterialFunction {
public:
double operator()(double pc) const;
};

class MRC_spline : public MaterialFunction {
public:
double operator()(double pc) const;
};

// Und die Klasse Material wird zu:
class Material {
public:
// ...
MaterialFunction* mrc;
// ...
};

// Aufruf:
double w = material.(*mrc)(pc); // sieht doof aus

Keiner zwingt dich, den Operator() zu verwenden. Eine Ableitung von
std::unary_function brauchst du ja eigentlich auch nicht, solange du
deine Klassen nicht mit den binders der Standardlib benutzt. Wenn du
darauf verzichtest, könnte es auch so aussehen:

doube w = material.mrc->calculate(pc);

Alternativ kannst du natürlich auch Funktion und Operator zur Verfügung
stellen:

class MaterialFunction : std::unary_function<double, double> {
public:
virtual double operator()(double) const = 0;
inline double calculate(double value) const
{
return operator()(value);
}
};


Quote:
Hier entscheidet jetzt der compilergenerierte Code anhand der
virtuellen Tabellen, welche Funktion genommen wird.

Ist vermutlich etwas langsamer als die switch/case-Variante. Wieviel das
ausmacht (bzw. ob es überhaupt was ausmacht) hängt stark vom
Gesamtproblem ab.

Quote:
------------------------------


3. Funktionszeiger (*brrr*)

Anstelle eines Wrappers verwende ich einen Funktionszeiger auf die
jeweilige Memberfunktion

class Material {
public:
typedef (Material::*MatFunc)(double);
// ...
double mrc_vG(double pc) const; // berechnet den Wert
analytisch
double mrc_spline(double pc) const; // berechnet den Wert
via
Spline-Interpolation
MatFunc mrc; // mrc = *mrc_vG or mrc = *mrc_spline;
// ...
};

// Aufruf:
double w = material.(*mrc)(pc); // sieht immer noch doof aus

Ich würde kaum einen Geschwindigkeitsunterschied zur Variante mit
virtuellen Funktionen erwarten.
Die virtuelle Funktion braucht üblicherweise zwei Dereferenzierungen
mehr, da erst der Objektzeiger dereferenziert wird, über ihn dann der
vtable-Pointer, der dann zum Funktionspointer führt, während Variante 3
den Funktionszeiger direkt verwenden kann. Andererseits handelt es ich
aber nicht um einen normalen Funktionszeiger, sondern um einen Zeiger
auf eine Memberfunktion, was die Situation wieder ändern könnte. Wie
das die Compiler für nicht-polymorphe Klassen implementieren, weiß ich
allerdings nicht.

Quote:
So, welche Variante ist wohl am schnellsten? Ich vermute ja mal
Variante 3,

Ich vermute eher Variante 1. Aber um Gewissheit zu erlangen solltest du
es einfach mal ausprobieren.

Quote:
aber die gefällt mir irgendwie nicht so recht (halt C-Style)...

Welche Variante würdet Ihr empfehlen?

Ich würde empfehlen, alle drei zu testen. Wenn der Unterschied sich als
signifikant herausstellen sollte, nimmst du die schnellste, sonst die,
die dir designmäßig am besten gefällt. Ein "richtiger" OO-Programmierer
würde dann wohl Variante 2 wählen ;-)

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de

Back to top
Rolf Magnus
Guest





PostPosted: Fri Nov 14, 2003 12:32 pm    Post subject: Re: Permformance vs. Flexibilität Reply with quote

Stefan Reuther wrote:

Quote:
Hallo,

Andreas Nicolai <Andreas.Nicolai (AT) web (DOT) de> wrote:
[Unterscheidung zwischen zwei Memberfunktionen]
1. einen Wrapper schreiben in der Art:

double Material::mrc(double pc) const {
switch (function_type) {
case ANALYTICAL : return mrc_vG(pc);
case SPLINE : return mrc_spline(pc);
default : throw runtime_error("Invalid function type!");
//
oder so ähnlich
}
};

Hier hast du die Entscheidung nur versteckt, nicht entfernt. Das
ist langsam.

Warum?

Quote:
Außerdem C-style Smile

Es kommt eben auf die Prioritäten an. Wenn es um solche Optimierungen
geht, muß man sich manchmal die Hände schmutzig machen :-)

Quote:
2. Für beide Funktionen Klassen polymorphe Klassen schreiben und die
Entscheidung durch compilergenerierten Code durchführen.

// Rein abstrakte Basisklasse
class MaterialFunction : std::unary_function<double, double> {
public:
double operator()(double) const = 0;
};

Das ist die Lösung, die ich wählen würde.

// Aufruf:
double w = material.(*mrc)(pc); // sieht doof aus

Und? Verpack's in eine inline-Funktion und gut ist.

3. Funktionszeiger (*brrr*)

Anstelle eines Wrappers verwende ich einen Funktionszeiger auf die
jeweilige Memberfunktion

class Material {
public:
typedef (Material::*MatFunc)(double);

So, welche Variante ist wohl am schnellsten? Ich vermute ja mal
Variante 3, aber die gefällt mir irgendwie nicht so recht (halt
C-Style)...

C-Style? In C gibts keine Member-Funktions-Zeiger :)

Variante 2 dürfte aber immer noch schneller sein, insbesondere,
wenn es irgendwo virtuelle Funktionen gibt, die ebenfalls auf
die Signatur passen.

Höchstens dann _könnte_ Variante 2 schneller sein.

Quote:
Außerdem ist Variante 2 flexibler. Ich weiß nicht, was dein
'Material' ist, aber falls irgendwann mal eine von 'Material'
abgeleitete Klasse hinzukommt, die eine weitere Berechnungs-
methode anbietet, ist das nur mit dieser Variante realisierbar.

Es ist mit allen Varianten realisierbar, nur müßte man auch existierende
Klassen ändern. Da ist eben wieder der beste Kompromiss zwischen Design
und Performance gefragt. Manchmal kann die Performance wichtiger sein.

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de

Back to top
Markus Breuer
Guest





PostPosted: Fri Nov 14, 2003 12:34 pm    Post subject: Re: Permformance vs. Flexibilität Reply with quote

Quote:
1. einen Wrapper schreiben in der Art:

double Material::mrc(double pc) const {
switch (function_type) {
case ANALYTICAL : return mrc_vG(pc);
case SPLINE : return mrc_spline(pc);
default : throw runtime_error("Invalid function type!");
//
oder so ähnlich
}
};


Von der Lösung würde ich eine sehr gute Performance erwarten. Wenn die
Funktionen kurz sind, könnten sie sogar noch inline expandiert werden,
was noch weiter beschleunigen könnte.

Von den drei angeboten Varianten ist diese die langsamste. Vor jedem
Aufruf der Funktion ist mindestens ein Vergleich mit function_type
notwendig.
Es ist doch zweifelsfrei effektiver, einmalig einen Funktionszeiger zu
setzen und im folgenden die Zielfunktion direkt aufzurufen. Der
Vergleich entfällt.
Dabei spielt es prinzipiell keine Rolle, ob der Funktionszeiger direkt
minipuliert wird, oder ob dieses implizit durch die Verwendung einer
polymorphen Funktion geschieht. Der resultierne Maschinen-Code ist der
gleiche!

Gruß Markus

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de

Back to top
Rolf Magnus
Guest





PostPosted: Fri Nov 14, 2003 4:56 pm    Post subject: Re: Permformance vs. Flexibilität Reply with quote

Markus Breuer wrote:

Quote:
1. einen Wrapper schreiben in der Art:

double Material::mrc(double pc) const {
switch (function_type) {
case ANALYTICAL : return mrc_vG(pc);
case SPLINE : return mrc_spline(pc);
default : throw runtime_error("Invalid function type!");
//
oder so ähnlich
}
};


Von der Lösung würde ich eine sehr gute Performance erwarten. Wenn
die Funktionen kurz sind, könnten sie sogar noch inline expandiert
werden, was noch weiter beschleunigen könnte.

Von den drei angeboten Varianten ist diese die langsamste. Vor jedem
Aufruf der Funktion ist mindestens ein Vergleich mit function_type
notwendig.

Der Vergleich selbst kann vernachlässigt werden, so daß höchstens der
darauf folgende bedingte Sprung Zeit kosten könnte (sofern man es so
überhaupt macht. Das ließe sich auch wegoptimieren). Da aber ein
Vergleich eigentlich nur wegen dem default sein muß, der aber
normalerweise nie auftritt, kann die branch prediction hier voll
zuschlagen und auch der Sprung wird sehr schnell.

Quote:
Es ist doch zweifelsfrei effektiver, einmalig einen Funktionszeiger zu
setzen und im folgenden die Zielfunktion direkt aufzurufen. Der
Vergleich entfällt.

So zweifelsfrei ist das nicht, aber nach dem, was ich jetzt bei meinem
g++ so als Resultat von switch/case gesehen habe, kann die
Funktionszeiger-Version wohl doch schneller sein.
Falls aber ein inlining möglich ist, kann Variante 1 trotzdem schneller
sein, da der Funktionszeiger ein Inlining prinzipbedingt ausschießt und
ein Funktionsaufruf nach meiner Erfahrung erstanulich teuer ist.

Quote:
Dabei spielt es prinzipiell keine Rolle, ob der Funktionszeiger direkt
minipuliert wird, oder ob dieses implizit durch die Verwendung einer
polymorphen Funktion geschieht. Der resultierne Maschinen-Code ist der
gleiche!

Der kann nur dann der gleiche sein, wenn die Funktion auch über einen
Zeiger auf einen Zeiger auf den Funktionszeiger aufgerufen wird.

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de

Back to top
Andreas Nicolai
Guest





PostPosted: Fri Nov 14, 2003 8:03 pm    Post subject: Permformance vs. Flexibilität Reply with quote

Hallo NG!

Habe eine Klasse Material, die verschiedene Materialfunktionen zur
Verfügung stellt. Diese können entweder analytische Funktionen oder
Splines sein:

class Material {
public:
// ...
double mrc_vG(double pc) const; // berechnet den Wert analytisch
double mrc_spline(double pc) const; // berechnet den Wert via
Spline-Interpolation
// ...
};


Welche der beiden Funktionen verwendet wird, ist erst zur Laufzeit bekannt
(z.B. durch eine Member-Variable 'function_type').
Jetzt müsste bei jeder Abfrage (und davon gibt es seeeeehr viele)
entschieden werden, welche der beiden Funktionen ich nun nehme.

Dabei fallen mir jetzt 3 Lösungen ein und eine Frage: welche Variante ist
wohl die Schnellste?

1. einen Wrapper schreiben in der Art:

double Material::mrc(double pc) const {
switch (function_type) {
case ANALYTICAL : return mrc_vG(pc);
case SPLINE : return mrc_spline(pc);
default : throw runtime_error("Invalid function type!"); //
oder so ähnlich
}
};

// Aufruf
double w = material.mrc(pc);
------------------------------


2. Für beide Funktionen Klassen polymorphe Klassen schreiben und die
Entscheidung durch compilergenerierten Code durchführen.

// Rein abstrakte Basisklasse
class MaterialFunction : std::unary_function<double, double> {
public:
double operator()(double) const = 0;
};

// Jetzt die beiden Deklarationen der eigentlichen Funktionen
class MRC_vG : public MaterialFunction {
public:
double operator()(double pc) const;
};

class MRC_spline : public MaterialFunction {
public:
double operator()(double pc) const;
};

// Und die Klasse Material wird zu:
class Material {
public:
// ...
MaterialFunction* mrc;
// ...
};

// Aufruf:
double w = material.(*mrc)(pc); // sieht doof aus
Hier entscheidet jetzt der compilergenerierte Code anhand der virtuellen
Tabellen, welche Funktion genommen wird.
------------------------------


3. Funktionszeiger (*brrr*)

Anstelle eines Wrappers verwende ich einen Funktionszeiger auf die
jeweilige Memberfunktion

class Material {
public:
typedef (Material::*MatFunc)(double);
// ...
double mrc_vG(double pc) const; // berechnet den Wert analytisch
double mrc_spline(double pc) const; // berechnet den Wert via
Spline-Interpolation
MatFunc mrc; // mrc = *mrc_vG or mrc = *mrc_spline;
// ...
};

// Aufruf:
double w = material.(*mrc)(pc); // sieht immer noch doof aus

------------------------------------------------

So, welche Variante ist wohl am schnellsten? Ich vermute ja mal Variante
3, aber die gefällt mir irgendwie nicht so recht (halt C-Style)...

Welche Variante würdet Ihr empfehlen?

Tschüß - Andreas

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de
Back to top
Andreas Nicolai
Guest





PostPosted: Fri Nov 14, 2003 8:50 pm    Post subject: Re: Permformance vs. Flexibilität Reply with quote

Hallo nochmal!

Danke Euch allen für Eure Meinungen und werde mich der Empfehlung
anschliessen und es einfach mal ausprobieren (Mist, damit sind dann wieder
2 Stunden wech :-)

Bis bald...
Andreas

PS: Für das Testen der Variante 3 bräuchte ich noch einen Tip -> siehe
Thread "Callbackfunktion"

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de
Back to top
Markus Breuer
Guest





PostPosted: Fri Nov 14, 2003 10:10 pm    Post subject: Re: Permformance vs. Flexibilität Reply with quote



Rolf Magnus schrieb:
Quote:
Markus Breuer wrote:

Von den drei angeboten Varianten ist diese die langsamste. Vor jedem
Aufruf der Funktion ist mindestens ein Vergleich mit function_type
notwendig.


Der Vergleich selbst kann vernachlässigt werden, so daß höchstens der
darauf folgende bedingte Sprung Zeit kosten könnte (sofern man es so
überhaupt macht. Das ließe sich auch wegoptimieren). Da aber ein
Vergleich eigentlich nur wegen dem default sein muß, der aber
normalerweise nie auftritt, kann die branch prediction hier voll
zuschlagen und auch der Sprung wird sehr schnell.

Du verlässt den Bereich von iso C++ und widmest dich speziellen
cpu-Architekturen. Ohne Frage wird ein direkter Funktionsaufruf
schneller sein, als ein bedingter. Daran ändert auch ein branch
prediction nichts. Im best-case sind die Varianten gleich schnell, im
worse case ist der direkte Aufruf schneller. Warum also nicht gleich die
sichere Seite?

Quote:
Falls aber ein inlining möglich ist, kann Variante 1 trotzdem schneller
sein, da der Funktionszeiger ein Inlining prinzipbedingt ausschießt und
ein Funktionsaufruf nach meiner Erfahrung erstanulich teuer ist.

Durch inlining sparst du dir einen function-call, nicht aber den Code
innerhalb der Funktion. Ich bleibe bei meiner Aussage:

foo();

ist schneller als

if ( ... )
foo();

Gruß Markus

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de

Back to top
Rolf Magnus
Guest





PostPosted: Sat Nov 15, 2003 2:27 am    Post subject: Re: Permformance vs. Flexibilität Reply with quote

Markus Breuer wrote:

Quote:


Rolf Magnus schrieb:
Markus Breuer wrote:

Von den drei angeboten Varianten ist diese die langsamste. Vor jedem
Aufruf der Funktion ist mindestens ein Vergleich mit function_type
notwendig.


Der Vergleich selbst kann vernachlässigt werden, so daß höchstens der
darauf folgende bedingte Sprung Zeit kosten könnte (sofern man es so
überhaupt macht. Das ließe sich auch wegoptimieren). Da aber ein
Vergleich eigentlich nur wegen dem default sein muß, der aber
normalerweise nie auftritt, kann die branch prediction hier voll
zuschlagen und auch der Sprung wird sehr schnell.

Du verlässt den Bereich von iso C++ und widmest dich speziellen
cpu-Architekturen.

Ja, genau wie du das bereits getan hast.

Quote:
Ohne Frage wird ein direkter Funktionsaufruf schneller sein, als ein
bedingter. Daran ändert auch ein branch prediction nichts.

Ja, aber ist ein indirekter Funktionsaufruf (über einen Zeiger) das auch
noch? Falls der Zeigerwert noch nicht in einem Register ist (und warum
sollte er?), müßte der auch noch extra aus dem Speicher gelesen werden,
was ihn auf jeden Fall langsamer macht, als eine erfolgreich
vorhergesehene Branch.
Theoretisch könnte man beim switch/case in bestimmten Fällen (wie dem
gezeigten) ganz ohne bedingten Sprung auskommen, aber ob das irgendein
Compiler macht, weiß ich nicht. Wahrscheinlich lohnt sich die Mühe aber
gar nicht, da der Unterschied einfach zu gering wäre. Ob das nun einen
Taktzyklus mehr oder weniger braucht, wird selten wichtig sein.

Im best-case sind die
Quote:
Varianten gleich schnell, im worse case ist der direkte Aufruf
schneller. Warum also nicht gleich die sichere Seite?

Falls aber ein inlining möglich ist, kann Variante 1 trotzdem
schneller sein, da der Funktionszeiger ein Inlining prinzipbedingt
ausschießt und ein Funktionsaufruf nach meiner Erfahrung erstanulich
teuer ist.

Durch inlining sparst du dir einen function-call, nicht aber den Code
innerhalb der Funktion.

Natürlich. Aber der function-call kann bei halbwegs kurzen Funktionen
durchaus mehr Zeit verbrauchen als der Inhalt.

Quote:
Ich bleibe bei meiner Aussage:

foo();

ist schneller als

if ( ... )
foo();

Was hat das mit der Frage zu tun?

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de

Back to top
Markus Breuer
Guest





PostPosted: Sat Nov 15, 2003 12:42 pm    Post subject: Re: Permformance vs. Flexibilität Reply with quote

Quote:
Rolf Magnus schrieb:

Markus Breuer wrote:


Ohne Frage wird ein direkter Funktionsaufruf schneller sein, als ein
bedingter. Daran ändert auch ein branch prediction nichts.


Ja, aber ist ein indirekter Funktionsaufruf (über einen Zeiger) das auch
noch? Falls der Zeigerwert noch nicht in einem Register ist (und warum
sollte er?), müßte der auch noch extra aus dem Speicher gelesen werden,
was ihn auf jeden Fall langsamer macht, als eine erfolgreich
vorhergesehene Branch.

Deine Argumentation geht davon aus, daß der Funktionszeiger nicht aus
dem Speicher geladen werden muß. Das mag durchaus stimmen, was aber ist
mit der Vergleichsvariablen für das switch/case? Diese musst du in jedem
Fall laden, um sie auszuwerten. Du holst dir also einen Status aus dem
Speicher und rufst bedingt auf.
Warum nicht gleich die Zieladresse aus dem Speicher holen und direkt
anspringen?

Quote:
Ich bleibe bei meiner Aussage:

foo();

ist schneller als

if ( ... )
foo();


Was hat das mit der Frage zu tun?

Ersteres ist die schematische Darstellung für den Aufruf durch einen
Funktionszeiger. Letzteres zeigt einen bedingten Aufruf, was in etwa
deinem switch/case entspricht.

Gruß Markus

--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de

Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ (German) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.