Message ID | 20190122134428.10208-1-laurent.pinchart@ideasonboard.com |
---|---|
State | Accepted |
Commit | a22dcaaa786de67786eeaeb47598ed76339249ad |
Headers | show |
Series |
|
Related | show |
Hi Laurent, Thanks for your nice investigation. On 2019-01-22 15:44:28 +0200, Laurent Pinchart wrote: > The compiler creates a move constructor automatically when none is > supplied, and it does the right thing by default in this case. Using > std::move() inside the function prevents the compiler from doing > return value optimization and actually hinders performances. Using > std::move() in the caller is unnecessary, the move constructor is used > automatically by the compiler. > > For all these reasons remove the tentative optimization that resulted in > worse performances and worse code. > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Looks good, thanks for sharing your test code. Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> > --- > > If anyone is interested, here's my test code. > > #include <iostream> > #include <string> > > class Copyable > { > public: > Copyable() > : name("default"), preserved("default") > { > std::cout << "Copyable::Copyable()" << std::endl; > } > > Copyable(const std::string &name) > : name(name), preserved(name) > { > std::cout << "Copyable::Copyable(\"" << name << "\")" << std::endl; > } > > Copyable(const Copyable &other) > : name(other.name) > { > std::cout << "Copyable::Copyable(const Copyable &" << name << ")" << std::endl; > } > > Copyable(Copyable &&other) > : name(std::move(other.name)) > { > std::cout << "Copyable::Copyable(Copyable &&" << name << ")" << std::endl; > } > > ~Copyable() > { > std::cout << "Copyable::~Copyable(" << name << ", " << preserved << ")" << std::endl; > } > > Copyable &operator=(const Copyable &other) > { > name = other.name; > std::cout << "Copyable::operator=(const Copyable &" << name << ")" << std::endl; > return *this; > } > > Copyable &operator=(Copyable &&other) > { > name = std::move(other.name); > std::cout << "Copyable::operator=(Copyable &&" << name << ")" << std::endl; > return *this; > } > > private: > std::string name; > std::string preserved; > }; > > class CopyableWrapper > { > public: > CopyableWrapper() { } > > CopyableWrapper(const std::string &name) > : value(name) > { > } > > Copyable value; > }; > > Copyable copy(const std::string &name) > { > return Copyable(name); > } > > Copyable move(const std::string &name) > { > return std::move(Copyable(name)); > } > > CopyableWrapper copy_wrap(const std::string &name) > { > return CopyableWrapper(name); > } > > int main(int, char **) > { > Copyable a; > Copyable b("b"); > Copyable c = Copyable("c"); > > a = b; > b = std::move(a); > > Copyable d = copy("d"); > Copyable e = move("e"); > > a = copy("f"); > b = std::move(move("g")); > c = std::move(copy("h")); > > CopyableWrapper w("w"); > > w = copy_wrap("x"); > > return 0; > } > > It produces the following output. > > Copyable::Copyable() > Copyable::Copyable("b") > Copyable::Copyable("c") > Copyable::operator=(const Copyable &b) > Copyable::operator=(Copyable &&b) > Copyable::Copyable("d") > Copyable::Copyable("e") > Copyable::Copyable(Copyable &&e) > Copyable::~Copyable(, e) > Copyable::Copyable("f") > Copyable::operator=(Copyable &&f) > Copyable::~Copyable(, f) > Copyable::Copyable("g") > Copyable::Copyable(Copyable &&g) > Copyable::~Copyable(, g) > Copyable::operator=(Copyable &&g) > Copyable::~Copyable(, ) > Copyable::Copyable("h") > Copyable::operator=(Copyable &&h) > Copyable::~Copyable(, h) > Copyable::Copyable("w") > Copyable::Copyable("x") > Copyable::operator=(Copyable &&x) > Copyable::~Copyable(, x) > Copyable::~Copyable(x, w) > Copyable::~Copyable(e, ) > Copyable::~Copyable(d, d) > Copyable::~Copyable(h, c) > Copyable::~Copyable(g, b) > Copyable::~Copyable(f, default) > > As you can see the "g" case it pretty bad, and the "h" case isn't better > than the "f" case. The "x" case shows that the move assignment operator > is used for the inner Copyable in CopyableWrapper automatically, which > implies that the compiler has generated a move assignment operator for > CopyableWrapper. > > src/cam/main.cpp | 2 +- > src/cam/options.cpp | 13 +------------ > src/cam/options.h | 2 -- > 3 files changed, 2 insertions(+), 15 deletions(-) > > diff --git a/src/cam/main.cpp b/src/cam/main.cpp > index 22211670c625..0d37039f5349 100644 > --- a/src/cam/main.cpp > +++ b/src/cam/main.cpp > @@ -33,7 +33,7 @@ static int parseOptions(int argc, char *argv[]) > parser.addOption(OptHelp, "Display this help message", "help"); > parser.addOption(OptList, "List all cameras", "list"); > > - options = std::move(parser.parse(argc, argv)); > + options = parser.parse(argc, argv); > if (!options.valid()) > return -EINVAL; > > diff --git a/src/cam/options.cpp b/src/cam/options.cpp > index d391a0e58436..82acff9bbeea 100644 > --- a/src/cam/options.cpp > +++ b/src/cam/options.cpp > @@ -102,7 +102,7 @@ OptionsParser::Options OptionsParser::parse(int argc, char **argv) > options.values_[c] = optarg ? optarg : ""; > } > > - return std::move(options); > + return options; > } > > void OptionsParser::usage() > @@ -160,17 +160,6 @@ OptionsParser::Options::Options() > { > } > > -OptionsParser::Options::Options(Options &&other) > - : values_(std::move(other.values_)) > -{ > -} > - > -OptionsParser::Options &OptionsParser::Options::operator=(Options &&other) > -{ > - values_ = other.values_; > - return *this; > -} > - > bool OptionsParser::Options::valid() const > { > return !values_.empty(); > diff --git a/src/cam/options.h b/src/cam/options.h > index 88336dfe3cc6..f99ea7300a71 100644 > --- a/src/cam/options.h > +++ b/src/cam/options.h > @@ -23,8 +23,6 @@ public: > class Options { > public: > Options(); > - Options(Options &&other); > - Options &operator=(Options &&other); > > bool valid() const; > bool isSet(int opt) const; > -- > Regards, > > Laurent Pinchart > > _______________________________________________ > libcamera-devel mailing list > libcamera-devel@lists.libcamera.org > https://lists.libcamera.org/listinfo/libcamera-devel
diff --git a/src/cam/main.cpp b/src/cam/main.cpp index 22211670c625..0d37039f5349 100644 --- a/src/cam/main.cpp +++ b/src/cam/main.cpp @@ -33,7 +33,7 @@ static int parseOptions(int argc, char *argv[]) parser.addOption(OptHelp, "Display this help message", "help"); parser.addOption(OptList, "List all cameras", "list"); - options = std::move(parser.parse(argc, argv)); + options = parser.parse(argc, argv); if (!options.valid()) return -EINVAL; diff --git a/src/cam/options.cpp b/src/cam/options.cpp index d391a0e58436..82acff9bbeea 100644 --- a/src/cam/options.cpp +++ b/src/cam/options.cpp @@ -102,7 +102,7 @@ OptionsParser::Options OptionsParser::parse(int argc, char **argv) options.values_[c] = optarg ? optarg : ""; } - return std::move(options); + return options; } void OptionsParser::usage() @@ -160,17 +160,6 @@ OptionsParser::Options::Options() { } -OptionsParser::Options::Options(Options &&other) - : values_(std::move(other.values_)) -{ -} - -OptionsParser::Options &OptionsParser::Options::operator=(Options &&other) -{ - values_ = other.values_; - return *this; -} - bool OptionsParser::Options::valid() const { return !values_.empty(); diff --git a/src/cam/options.h b/src/cam/options.h index 88336dfe3cc6..f99ea7300a71 100644 --- a/src/cam/options.h +++ b/src/cam/options.h @@ -23,8 +23,6 @@ public: class Options { public: Options(); - Options(Options &&other); - Options &operator=(Options &&other); bool valid() const; bool isSet(int opt) const;
The compiler creates a move constructor automatically when none is supplied, and it does the right thing by default in this case. Using std::move() inside the function prevents the compiler from doing return value optimization and actually hinders performances. Using std::move() in the caller is unnecessary, the move constructor is used automatically by the compiler. For all these reasons remove the tentative optimization that resulted in worse performances and worse code. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> --- If anyone is interested, here's my test code. #include <iostream> #include <string> class Copyable { public: Copyable() : name("default"), preserved("default") { std::cout << "Copyable::Copyable()" << std::endl; } Copyable(const std::string &name) : name(name), preserved(name) { std::cout << "Copyable::Copyable(\"" << name << "\")" << std::endl; } Copyable(const Copyable &other) : name(other.name) { std::cout << "Copyable::Copyable(const Copyable &" << name << ")" << std::endl; } Copyable(Copyable &&other) : name(std::move(other.name)) { std::cout << "Copyable::Copyable(Copyable &&" << name << ")" << std::endl; } ~Copyable() { std::cout << "Copyable::~Copyable(" << name << ", " << preserved << ")" << std::endl; } Copyable &operator=(const Copyable &other) { name = other.name; std::cout << "Copyable::operator=(const Copyable &" << name << ")" << std::endl; return *this; } Copyable &operator=(Copyable &&other) { name = std::move(other.name); std::cout << "Copyable::operator=(Copyable &&" << name << ")" << std::endl; return *this; } private: std::string name; std::string preserved; }; class CopyableWrapper { public: CopyableWrapper() { } CopyableWrapper(const std::string &name) : value(name) { } Copyable value; }; Copyable copy(const std::string &name) { return Copyable(name); } Copyable move(const std::string &name) { return std::move(Copyable(name)); } CopyableWrapper copy_wrap(const std::string &name) { return CopyableWrapper(name); } int main(int, char **) { Copyable a; Copyable b("b"); Copyable c = Copyable("c"); a = b; b = std::move(a); Copyable d = copy("d"); Copyable e = move("e"); a = copy("f"); b = std::move(move("g")); c = std::move(copy("h")); CopyableWrapper w("w"); w = copy_wrap("x"); return 0; } It produces the following output. Copyable::Copyable() Copyable::Copyable("b") Copyable::Copyable("c") Copyable::operator=(const Copyable &b) Copyable::operator=(Copyable &&b) Copyable::Copyable("d") Copyable::Copyable("e") Copyable::Copyable(Copyable &&e) Copyable::~Copyable(, e) Copyable::Copyable("f") Copyable::operator=(Copyable &&f) Copyable::~Copyable(, f) Copyable::Copyable("g") Copyable::Copyable(Copyable &&g) Copyable::~Copyable(, g) Copyable::operator=(Copyable &&g) Copyable::~Copyable(, ) Copyable::Copyable("h") Copyable::operator=(Copyable &&h) Copyable::~Copyable(, h) Copyable::Copyable("w") Copyable::Copyable("x") Copyable::operator=(Copyable &&x) Copyable::~Copyable(, x) Copyable::~Copyable(x, w) Copyable::~Copyable(e, ) Copyable::~Copyable(d, d) Copyable::~Copyable(h, c) Copyable::~Copyable(g, b) Copyable::~Copyable(f, default) As you can see the "g" case it pretty bad, and the "h" case isn't better than the "f" case. The "x" case shows that the move assignment operator is used for the inner Copyable in CopyableWrapper automatically, which implies that the compiler has generated a move assignment operator for CopyableWrapper. src/cam/main.cpp | 2 +- src/cam/options.cpp | 13 +------------ src/cam/options.h | 2 -- 3 files changed, 2 insertions(+), 15 deletions(-)