Programmierbuddies C++/Boost/C++11

Hier der Benchmark:

ohne unique ptr und mit const& und RVO:
real 0m3.510s
user 0m7.010s
sys 0m1.200s


real 0m3.519s
user 0m7.137s
sys 0m1.197s


mit unique_ptr:
real 0m2.925s
user 0m7.160s
sys 0m1.263s


real 0m2.790s
user 0m7.093s
sys 0m1.300s


Wie vorher gesagt die Performance ändert sich nicht wesentlich. Damit ist das Thema geklärt.
 
Mit so Benchmarks muss man sehr vorsichtig sein, insbesondere wenn IO im Spiel ist. Einige Hinweise, die ich beachten würde (von denen du manches vlt schon beachtet hast:
  • tmpfs bzw. shm (unter linux) nehmen um disk-caching effekte zu vermeiden (dann misst du deine algorithmus, bekommst aber vzerrte Aergebnisse, falls die Programm real unterschiedlich viel IO machen)
  • Datensätze nehmen, die so groß sind, dass man auch etwas sieht, bei deinem Beispiel ist der Unterschied 20%, das ist sehr signifikant, aber da es eine halbe Sekunde ist, könnte es eben auch der Chrome-Browser gewesen sein, der im Hintergrund seinen Cache neu geschrieben hat oder so
  • immer weniger threads nehmen als es cores gibt und gucken, dass nichts anderes läuft (eigentlich klar)

Zur Sache kann ich noch sagen: es rentiert sich fast immer garkeine Rückgabewerte zu benutzen, sondern einfach das zu verändernde als Referenz zu übergeben. Dann brauch man sich über RVO und Kopien keine Gedanken zu machen (zumindest nicht an der Stelle).
 
Ein "return std::move(…)" ist also immer sinnlos. Ohne Ausnahme.
Kannst du das bitte etwas erläutern. Oder anders gesagt: Ich glaube das nicht so recht da ich denke dass das von der Implementierung der zurückzugebenden Klasse abhängt. Klar, kopiert werden muss in jedem Falle, die Frage ist aber wie. Wenn es da keinen Unterschied zwischen dem ctor(const T&) und dem ctor(const T&&) gibt, spielt das keine Rolle, aber der ctor(const T&&) bietet ja durchaus Optimierungsmöglichkeiten.

Oder wird im Falle der Rückgabe per Value automatisch constT&& aufgerufen?
Oder denke ich da einfach falsch ?
 
T && foo heißt ja das foo ein rvalue ist. In dem Moment indem etwas aus einem return kommt ist es immer ein rvalue.
Wenn Du also T bar(…) deklarierst gibt bar T && zurück. Du kannst das testen falls Du T mit ein paar Methoden ausstattest:
Code:
inline bool is_rval() && { return true; }
inlne bool is_rval() { return false; }

Anders ist das natürlich wenn Du eine Referenz zurück gibst. Das impliziert ja, dass das kein rvalue ist.
 
OK, ich verstehe. Werde das gelegentlich mal austesten. Andererseits: return std::move ist dann zwar überflüssig, aber auch kein Schaden da es ja nur ein cast ist und evtl. die Sache deutlicher macht. C++ ist voller Überraschungen! :rolleyes: Wie auch immer...

Danke!
 
Ein "return std::move(…)" ist also immer sinnlos. Ohne Ausnahme.

Deine Aussage ist falsch, es reicht ein Gegenbeispiel;).
Außerdem behauptest und folgerst du schon wieder ziemlich Steil ohne Beweis, unschön.


1. Fall (Hier zum direkten Testen http://www.tutorialspoint.com/compile_cpp11_online.php):

#include <iostream>
class foo{
public:
foo() { std::cout << "constructed\n";}
foo(const foo&) { std::cout << "copy ctor\n";}
foo(foo&&) { std::cout << "move ctor\n";}
};

foo mve(foo&& f){
//...stuff
return std::move(f);
}

foo cpy(foo&& f){
return f;
}

int main()
{
foo f;
cpy(std::move(f)); //output "copy ctor"
mve(std::move(f)); // output "move ctor"
}

Siehe letzter Beitrag, analoges Praxis Beispiel : http://stackoverflow.com/questions/...ning-a-value-from-a-function-to-avoid-to-copy

2.Fall
class foo{
//Ctors...
void process(); //work on ptr data
std::unique_ptr<data> getResults() { return std::move(ptr);} //ohne move gibt es hier eine Fehlermeldung, da ptr nicht Funktionslokales Objekt ist.

private:
std::unique_ptr<data> ptr;
//members...
}

Die Klasse foo wird nach dem getResults() call nicht mehr verwendet.
 
Deine Aussage ist falsch, es reicht ein Gegenbeispiel;).
Außerdem behauptest und folgerst du schon wieder ziemlich Steil ohne Beweis, unschön.


1. Fall (Hier zum direkten Testen http://www.tutorialspoint.com/compile_cpp11_online.php):

Code:
...
foo mve(foo&& f){
      //...stuff
return std::move(f);
}

foo cpy(foo&& f){
    return f;
}
...

Siehe letzter Beitrag, analoges Praxis Beispiel : http://stackoverflow.com/questions/...ning-a-value-from-a-function-to-avoid-to-copy
Hier würde ich davon ausgehen, dass das ein Compiler-Fehler oder ein Fehler im Standard ist. A & a wäre eine Referenz auf ein Objekt außerhalb des Methodenkontext und damit ein lvalue. A && a ist auch eine Referenz und der Compiler übersieht anscheinend die rvalue Garantie und erzeugt eine Kopie damit er da überhaupt ein Return machen kann.

Ob das nach Standard korrekt ist weiß ich nicht (ich würde davon ausgehen, dass dem nicht so ist) aber im Sinne des Erfinders ist es auf jeden Fall nicht. Interessant wäre hier mal verschiedene Compiler zu vergleichen.

Da man hier eine Referenz bekommt würde ich an der Stelle aber sowieso eine Referenz zurückgeben.

2.Fall
Code:
class foo{
 //Ctors...
 void process(); //work on ptr data
 std::unique_ptr<data> getResults() { return std::move(ptr);}  //ohne move gibt es hier eine Fehlermeldung, da ptr nicht Funktionslokales Objekt ist.
...
Da hast Du Recht. In deinem Fall geht es jedoch um lokale Instanzen (oder um Instanzen die lokal sein sollten, die Du aber auf den Heap schmeißt und dann unique_ptr drumrumbastelst damit sie sich wie lokale instanzen verhalten).

Ich muss Dir recht geben, dass ich mich hier falsch ausgedrückt habe.

Deswegen mache ich es noch mal vorsichtiger:

Ich kenne keine Fälle wo es sinnvoll ist ein "return std::move()" zu machen. Wenn das notwendig wird ist das in der Regel ein Red-flag, dass man irgend etwas grundsätzlich falsch macht. Ähnlich wie bei mutable. Das habe ich auch schon mal verwendet und dann noch mal drüber nachgedacht und es richtig gemacht.
 
Hier der Benchmark:
Das ist kein Benchmark, sondern eine Auflistung von Zahlen gleicher Dimension.

Du hast weder die beiden Codes veröffentlicht, noch hast du beschrieben, wie das Arbeitsmaterial des Benchmarks aussieht (oder besser: generiert werden kann). Desweiteren fehlen die genauen Aufrufe der beiden Programme und die Definition der Laufzeitumgebung.

Wer misst, misst oft Mist.

Rob
 
So, ich glaube wir können trotzdem alle wieder etwas freundlicher werden. Der OP hatte bei einem Projekt um Unterstützung bzw. Rat gebeten.
Sowohl beim Erteilen als auch beim Annehmen von Rat, kann man entspannter sein, dann klappts vielleicht auch mit Optimieren! und andere Lesende lernen noch was dabei.
 
Hier würde ich davon ausgehen, dass das ein Compiler-Fehler oder ein Fehler im Standard ist. A & a wäre eine Referenz auf ein Objekt außerhalb des Methodenkontext und damit ein lvalue. A && a ist auch eine Referenz und der Compiler übersieht anscheinend die rvalue Garantie und erzeugt eine Kopie damit er da überhaupt ein Return machen kann.

Ob das nach Standard korrekt ist weiß ich nicht (ich würde davon ausgehen, dass dem nicht so ist) aber im Sinne des Erfinders ist es auf jeden Fall nicht. Interessant wäre hier mal verschiedene Compiler zu vergleichen.

Du wunderst dich warum RVO in diesem Beispiel nicht funktioniert. Schau dir die Definition an. Und Denk an die RValue Referenz.

Hilfestellung:
http://en.cppreference.com/w/cpp/language/value_category

Der Output war:
constructed
copy ctor
move ctor


Hier der Vergleich:
http://webcompiler.cloudapp.net/ (Visual Studio 2015) -> gleicher output
clang 3.6.1 -> gleicher output
gcc 5.1.0 -> gleicher output


Hinweis 1: wie ist move implementiert?
Hinweis 2: Den Code nun nochmal jetzt ohne RValue Referenz nun mit LValue Referenz

//---------------------------------------------------------------------------------
#include <iostream>
class foo{
public:
foo() { std::cout << "constructed\n";}
foo(const foo&) { std::cout << "copy ctor\n";}
foo(foo&&) { std::cout << "move ctor\n";}
};

foo mve(foo&& f){
//...stuff
return std::move(f);
}

foo cpy(foo& f){
return f;
}

int main()
{
foo f;
cpy(f); //output "copy ctor"
mve(std::move(f)); // output "move ctor"
}

//---------------------------------------------------------------------------------

Spoiler:
constructed
copy ctor
move ctor



Ich kenne keine Fälle wo es sinnvoll ist ein "return std::move()" zu machen.
Teil deines Coding Style Guides. Mehr nicht.


Ähnlich wie bei mutable. Das habe ich auch schon mal verwendet und dann noch mal drüber nachgedacht und es richtig gemacht.
Auch Teil deines Coding Style Guides. Mehr nicht.

Anwendungsbeispiele:
- Interne Statistiken
- Caching
- Mutex
- ...

Lohnt sich mal da reinzuschauen:
http://herbsutter.com/2013/05/24/gotw-6a-const-correctness-part-1-3/
https://channel9.msdn.com/posts/C-and-Beyond-2012-Herb-Sutter-You-dont-know-blank-and-blank
 
eul3r schrieb:
Anwendungsbeispiele:
- Interne Statistiken
Dafür würde ich kein "mutable" nehmen. Einmal ist es in nebenläufigen Situationen fischig, man müsste es zwischen Locks packen und das führt zu Lock-Contention. Außerdem darf man bei sowas auch nie Cache-Effekte ignorieren, gerade bei komplexen Systemen mit nichttrivialer CPU-Anordnung ist der Overhead gewaltig. Konstantin Belousov hatte da mal diesen kranken Vorschlag, den man sich schamlos abkupfern und auf Userland-Code adaptieren kann:
Code:
__asm __volatile("addq\t%1,%%gs:(%0)"
:
: "r" ((char *)c - (char *)&__pcpu[0]), "r" (inc)
: "memory", "cc");
Das ist eine einzige, lockfreie Instruktion. Die Daten sind lokal zur jeweiligen CPU, was Cache-Effekte weitgehend ausschließt.
 
Auch interessant, folgender Code jetzt mit templates, da sieht es wieder anders aus:

Hinweis: Es wurden die Funktionen mve und cpy in template Funktionen geändert.

#include <iostream>
class foo{
public:
foo() { std::cout << "constructed\n";}
foo(const foo&) { std::cout << "copy ctor\n";}
foo(foo&&) { std::cout << "move ctor\n";}
};

template <typename T>
T mve(T&& f){
//...stuff
return std::move(f);
}

template <typename T>
T cpy(T&& f){ <----hier wird jetzt kein Copy CTor aufgerufen sondern RVO
return f;
}

int main()
{
foo f;
cpy(f); //output "copy ctor"
mve(std::move(f)); // output "move ctor"
}

Output:
constructed
move ctor

Scott Meyers schreibt in seinem Buch Effektive Modern C++ in Item 25 auf S. 173 genau über die Thematik (Matrix operator+(Matrix&& lhs, const Matrix& rhs) { lhs +=rhs; return std::move(lhs);} da es sonst kopiert wird).

Spannend ist auch noch, dass RVO nicht immer zieht, wenn man erwarten würde. Dann gilt laut Standard (Zitat Meyers), dass zumindest return std::move() durch geführt wird. D.h. der Compiler macht das implizit.

Als Vertiefung bietet sich oben genanntes Buch Kapitel 5, Item 23 - Item 30 an.
 
https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers schrieb:
Code:
template<typename T>
void f(T&& param);
...
int x;
...
f(10);                           // invoke f on rvalue
f(x);                            // invoke f on lvalue

Because of the reference-to-reference, this instantiated code is prima facie invalid, but the source code– “f(x)” – is completely reasonable. To avoid rejecting it, C++11 performs “reference collapsing” when references to references arise in contexts such as template instantiation.
Ich finde das ja gar nicht completely reasonable. Ich denke der Compiler sollte einem das f(x) um die Ohren hauen.

Ich gehe sogar so weit zu sagen, dass ich das schockierend finde.
 
Ich finde das ja gar nicht completely reasonable. Ich denke der Compiler sollte einem das f(x) um die Ohren hauen.

Ich gehe sogar so weit zu sagen, dass ich das schockierend finde.

Fand ich am Anfang auch, und es ist ein komplexeren Situationen auch nicht sehr leicht zu durchblicken, aber es macht in vielen banalen Situationen total Sinn:
Code:
auto && var = getValue(fooobar);
Das verhält sich optimal, egal ob getValue für fooobar eine Kopie oder eine Referenz zurückgibt. Gerade bei templatisiertem Code, wo die Funktion für unterschiedliche Paramter unterschiedliches zurückgibt ist das praktisch.

Vorsicht ist geboten bei:
Code:
 template <typename std::enable_if<IsNumericalConcept<T, int>::type = 0>
T plus3(T && t) { return t +3; }
Wenn man das nämlich mit mit int c = 0; d = plus3(c); aufruft, dann ist T == int & was dazu führt, dass dein Concept-Checking scheitert. Muss man in dem SFINAE clause halt noch ein remove_ref reinmachen. Das müsste man aber nicht wenn der Typ feststeht, oder bei einer l-value-reference..
 
Ich denke es wäre hier naheliegender

Code:
template <T> T f(T && t);
template <T> T f(T & t);

zu implementieren. Ist vielleicht etwas mehr Code aber das nimmt man ja auch in kauf wenn man Methoden doppelt braucht, weil sie aus const und nicht const Methoden aufgerufen werden.

Jedenfalls kann man dann auf solch subtile Konstrukte verzichten.

Das man außerhalb von Signaturen RValues erzeugen kann finde ich auch krank. Ein RValue mit Namen leuchtet mir nicht ein.
 
Ich denke es wäre hier naheliegender

Code:
template <T> T f(T && t);
template <T> T f(T & t);

zu implementieren. Ist vielleicht etwas mehr Code aber das nimmt man ja auch in kauf wenn man Methoden doppelt braucht, weil sie aus const und nicht const Methoden aufgerufen werden.
Genau das muss man bei const ja auch nicht machen, wenn man templatisierte Funktionen verwendet. Da verhält es sich analog, siehe dazu auch:
http://www.hekster.org/Professional/Universal-References.html
 
Zurück
Oben