Hi!
I am using VC++ 7.1 and have a question about return value optimization.
Consider the following code:
#include <list>
#include <string>
struct test
{
std::list <std::string> m_strings;
};
test func1 (int r)
{
if (r == 0)
return test ();
test t = func1 (r - 1);
t.m_strings.push_back ("lala");
return t;
}
int main ()
{
test t = func1 (3);
}
Now, stepping thru the code, one would assume that 'test' objects are
copy constructed and destroyed often. My question is, would a decent
compiler, like VC++ 7.1, be able to optimize this in such a way that only a
single 'test' object is used throughout the code? To make this post more
on-topic, would the Standard even allow such an optimization, as it is a
pretty great change to the original code ..
Thanks in advance!
--
jb
(replace y with x if you want to reply by e-mail) 20 2293
Jakob Bieling wrote: Hi!
I am using VC++ 7.1 and have a question about return value optimization. Consider the following code:
#include <list> #include <string>
struct test { std::list <std::string> m_strings; };
test func1 (int r) { if (r == 0) return test ();
test t = func1 (r - 1); t.m_strings.push_back ("lala"); return t; }
int main () { test t = func1 (3); }
Now, stepping thru the code, one would assume that 'test' objects are copy constructed and destroyed often. My question is, would a decent compiler, like VC++ 7.1, be able to optimize this in such a way that only a single 'test' object is used throughout the code? To make this post more on-topic, would the Standard even allow such an optimization, as it is a pretty great change to the original code ..
The standard allows the optimization (6.3.3).
"Jeff Schwab" <je******@comcast.net> wrote in message
news:bt********************@comcast.com... The standard allows the optimization (6.3.3).
In my copy, 6.3 is "Compound statement or block" and only has a single
paragraph. IOW, there is no 6.3.3?
regards
--
jb
(replace y with x if you want to reply by e-mail)
Jakob Bieling wrote: Hi!
I am using VC++ 7.1 and have a question about return value optimization. Consider the following code:
#include <list> #include <string>
struct test { std::list <std::string> m_strings; };
test func1 (int r) { if (r == 0) return test ();
test t = func1 (r - 1); t.m_strings.push_back ("lala"); return t; }
int main () { test t = func1 (3); }
As Jeff said, in the above example the compiler is allowed to optimise
away unnecessary locals (in func1()), temporaries and the corresponding
copy constructor calls. If func1() were defined like this:
test func1 (int r)
{
test t;
t.m_strings.push_back ("lala");
return t;
}
with the same main(), I would definitely expect just one construction and
destruction from a decent compiler. The recursion, however, may complicate
things a bit. You can add a couple of ctors to test (default and copy) and
a dtor that cout a message and see.
Consider this too:
int main()
{
test t;
//do something to t ...
t = func1 (3);
}
Not much luck in this case: t will be constructed first, then at least one
object of class test will be created as a result of func1(), then operator =
will be called.
When I care about performance, I usually define func1() as
void func1 (int r, test& t) {...}
If the functional notation were important, I would consider using proxies
(and accomodate the "actual" objects acordingly), but I would not return
really big objects from a function.
Denis
"Denis Remezov" <de*********************@yahoo.ca.removethis> wrote in
message news:40***************@yahoo.ca.removethis... When I care about performance, I usually define func1() as void func1 (int r, test& t) {...} If the functional notation were important,
Well, it is not important, but I do care about design and the above
seems like a rather ugly solution; tho it would serve the purpose pretty
well.
I would consider using proxies (and accomodate the "actual" objects acordingly), but I would not return really big objects from a function.
It is not that the object is big, but it contains a linked list of
non-POD objects, so creating copies of it where it is not necessary, seems
like a bad idea.
I assume the proxy would take care of doing a fast swap rather than a
deep-copy of the objects, correct?
Thanks!
--
jb
(replace y with x if you want to reply by e-mail)
"Jakob Bieling" <ne*****@gmy.net> wrote... "Jeff Schwab" <je******@comcast.net> wrote in message news:bt********************@comcast.com...
The standard allows the optimization (6.3.3).
In my copy, 6.3 is "Compound statement or block" and only has a single paragraph. IOW, there is no 6.3.3?
Jeff probably meant 6.6.3 "The return statement".
V
Jakob Bieling wrote: "Jeff Schwab" <je******@comcast.net> wrote in message news:bt********************@comcast.com...
The standard allows the optimization (6.3.3).
In my copy, 6.3 is "Compound statement or block" and only has a single paragraph. IOW, there is no 6.3.3?
regards
It's not in mine (2003 revision) either...
- Pete
"Victor Bazarov" <v.********@comAcast.net> wrote in message
news:7nygc.20953$ru4.18150@attbi_s52... "Jakob Bieling" <ne*****@gmy.net> wrote... "Jeff Schwab" <je******@comcast.net> wrote in message news:bt********************@comcast.com...
The standard allows the optimization (6.3.3).
In my copy, 6.3 is "Compound statement or block" and only has a
single paragraph. IOW, there is no 6.3.3?
Jeff probably meant 6.6.3 "The return statement".
Thanks .. there is a reference to 12.2 which explicitly allows the
optimization of constructing the return value directly in place of the
variable that is used to hold the return value, thus eliminating the
temporary copy.
Guess I will go for Dennis' proxy-class approach, since it does not look
like a decent compiler will do the optimization I am after.
Thanks for the comments!
--
jb
(replace y with x if you want to reply by e-mail)
On Sun, 18 Apr 2004 19:38:26 +0200 in comp.lang.c++, "Jakob Bieling"
<ne*****@gmy.net> wrote, Guess I will go for Dennis' proxy-class approach, since it does not look like a decent compiler will do the optimization I am after.
A decent compiler will certainly do that optimization!
Now the question remains, whether there is one yet that does? Perhaps
not.
"David Harmon" <so****@netcom.com> wrote in message
news:40****************@news.west.earthlink.net... On Sun, 18 Apr 2004 19:38:26 +0200 in comp.lang.c++, "Jakob Bieling" <ne*****@gmy.net> wrote, Guess I will go for Dennis' proxy-class approach, since it does not
looklike a decent compiler will do the optimization I am after.
A decent compiler will certainly do that optimization! Now the question remains, whether there is one yet that does? Perhaps not.
;)
--
jb
(replace y with x if you want to reply by e-mail)
Jakob Bieling wrote: "Denis Remezov" <de*********************@yahoo.ca.removethis> wrote in message news:40***************@yahoo.ca.removethis...
When I care about performance, I usually define func1() as void func1 (int r, test& t) {...} If the functional notation were important, Well, it is not important, but I do care about design and the above seems like a rather ugly solution;
[snip]
It depends. In some respect, the syntax is inferior, I agree; func1() now
also needs to be careful to handle or dispose of the existing contents
of t properly (that could be a bad thing). Potential advantages are
(performance issues aside):
0) Class test does not need to be constructible in the given scope.
1) It is further guaranteed and made explicit that no construction
or copying of objects of class test will take place. I would consider using proxies (and accomodate the "actual" objects acordingly), but I would not return really big objects from a function.
It is not that the object is big, but it contains a linked list of non-POD objects,
[snip]
Sorry I didn't make it clear, but I meant to include that into the
definition of a "big object". I assume the proxy would take care of doing a fast swap rather than a deep-copy of the objects, correct?
Something of the kind. I was thinking about proxy objects sharing pointers
to the "real" objects and reference-counting them, deferring the
actual creation and copying until such point in time that a write-operation
is requested.
Denis
On Sun, 18 Apr 2004 18:30:47 +0200 in comp.lang.c++, "Jakob Bieling"
<ne*****@gmy.net> wrote, When I care about performance, I usually define func1() as void func1 (int r, test& t) {...} If the functional notation were important,
Well, it is not important, but I do care about design and the above seems like a rather ugly solution; tho it would serve the purpose pretty well.
I am sure your example is simplified and that there would be other
concerns in a complex application. Nevertheless, for the purpose of the
simple example I would consider constructing a single object and using a
member function with the implied 'this' to make it perhaps a bit less
ugly than the explicit reference argument. In my view, this gets closer
to the implementation originally desired, albeit at the cost of a member
function that you might prefer as a non-menber.
#include <iostream>
using std::cerr;
#include <list>
#include <string>
struct test {
std::list<std::string> m_strings;
test & func1(int r);
// instrumentation:
test() { cerr << "Construct!\n"; }
test(const test & x)
: m_strings(x.m_strings)
{ cerr << "Copy construct " << m_strings.size() << "!\n"; }
~test() { cerr << "Destruct " << m_strings.size() << "!\n"; }
//
};
test & test::func1(int r)
{
if (r == 0)
return *this;
func1(r - 1);
m_strings.push_back("lala");
return *this;
}
int main()
{
test t;
t.func1(3);
}
Victor Bazarov wrote: "Jakob Bieling" <ne*****@gmy.net> wrote...
"Jeff Schwab" <je******@comcast.net> wrote
The standard allows the optimization (6.3.3).
In my copy, 6.3 is "Compound statement or block" and only has a single paragraph. IOW, there is no 6.3.3?
Jeff probably meant 6.6.3 "The return statement".
Thanks, Victor. You're right, I did mean 6.6.3.
On Sun, 18 Apr 2004 14:57:20 +0200, "Jakob Bieling" <ne*****@gmy.net>
wrote: Hi!
I am using VC++ 7.1 and have a question about return value optimization. Consider the following code:
#include <list> #include <string>
struct test { std::list <std::string> m_strings; };
test func1 (int r) { if (r == 0) return test ();
The line above disables the optimization for most compilers.
Generally, for NRVO to work, all return statements must return the
same object (although this isn't a requirement of 12.8/15, just common
practice). test t = func1 (r - 1); t.m_strings.push_back ("lala"); return t;
So the above probably won't be optimized.
}
int main () { test t = func1 (3); }
Now, stepping thru the code, one would assume that 'test' objects are copy constructed and destroyed often. My question is, would a decent compiler, like VC++ 7.1, be able to optimize this in such a way that only a single 'test' object is used throughout the code? To make this post more on-topic, would the Standard even allow such an optimization, as it is a pretty great change to the original code ..
Both GCC and VC7.1 failed to optimize the original code (I added
instrumentation). Here is a version that on GCC 3.3 prints:
Default
Destroy
which is what you were after. VC++ 7.1 doesn't implement NRVO, so that
prints:
Default
Copy
Destroy
Copy
Destroy
Copy
Destroy
Copy
Destroy
Copy
Destroy
Copy
Destroy
Copy
Destroy
Copy
Destroy
Destroy
Oh dear! Here's the code:
#include <list>
#include <string>
#include <iostream>
using namespace std;
struct test
{
test()
{
cout << "Default\n";
}
test(test const& t)
:m_strings(t.m_strings)
{
cout << "Copy\n";
}
~test()
{
cout << "Destroy\n";
}
test& operator=(test const& t)
{
m_strings = t.m_strings;
cout << "Assign\n";
return *this;
}
std::list <std::string> m_strings;
};
test func1 (int r)
{
test t(r == 0? test() : func1 (r - 1));
if (r != 0)
t.m_strings.push_back ("lala");
return t;
}
int main ()
{
test t = func1 (3);
}
And what's the moral of this? Well, to enable NRVO, make sure you:
1. Ensure all return statements return the same local object.
2. Avoid assignments.
3. Use GCC.
I assume MS will implement NRVO eventually...
Tom
--
C++ FAQ: http://www.parashift.com/c++-faq-lite/
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
tom_usenet wrote: Oh dear! Here's the code:
cat main.cc
#include <list>
#include <string>
#include <iostream>
struct test {
test(void) {
std::cout << "Default" << std::endl;
}
test(test const& t):
m_strings(t.m_strings) {
std::cout << "Copy" << std::endl;
}
~test(void) {
std::cout << "Destroy" << std::endl;
}
test& operator=(test const& t) {
m_strings = t.m_strings;
std::cout << "Assign" << std::endl;
return *this;
}
std::list <std::string> m_strings;
};
test func1(int r) {
test t(r == 0? test(): func1(r - 1));
if (r != 0)
t.m_strings.push_back("lala");
return t;
}
int main (int argc, char* argv[]) {
test t = func1(3);
return 0;
}
g++ -Wall -ansi -pedantic -O2 -o main main.cc ./main
Default
Destroy
David Harmon wrote: I am sure your example is simplified and that there would be other concerns in a complex application. Nevertheless, for the purpose of the simple example I would consider constructing a single object and using a member function with the implied 'this'
This is *very* bad advice.
to make it perhaps a bit less ugly than the explicit reference argument. In my view, this gets closer to the implementation originally desired, albeit at the cost of a member function that you might prefer as a non-menber.
#include <iostream> // using std::cerr; #include <list> #include <string>
struct test {
using std::cerr;
std::list<std::string> m_strings; test & func1(int r);
// instrumentation: test() { cerr << "Construct!\n"; } test(const test & x): m_strings(x.m_strings) { cerr << "Copy construct " << m_strings.size() << "!\n"; } ~test() { cerr << "Destruct " << m_strings.size() << "!\n"; } // };
test& test::func1(int r) {
if (0 == r) return *this;
func1(r - 1); m_strings.push_back("lala"); return *this; }
int main() { test t; t.func1(3);
return 0; }
David Harmon wrote: A decent compiler will certainly do that optimization! Now the question remains, whether there is one yet that does? Perhaps not.
My GNU C++ compiler:
g++ --version
g++ (GCC) 3.2 20020903 (Red Hat Linux 8.0 3.2-7)
implements the NRVO. So do several other quality C++ compilers.
If your compiler does not implement the NVRO,
you should be shopping for a better compiler.
"tom_usenet" <to********@hotmail.com> wrote in message
news:uf********************************@4ax.com... On Sun, 18 Apr 2004 14:57:20 +0200, "Jakob Bieling" <ne*****@gmy.net> test func1 (int r) { if (r == 0) return test ();
The line above disables the optimization for most compilers. Generally, for NRVO to work, all return statements must return the same object (although this isn't a requirement of 12.8/15, just common practice).
Compilers should allow NVRO if the different return statements return
objects declared after the previous return statement.
Maybe explicit braces will help, but I'm not sure:
test func1 (int r)
{
if (r == 0)
return test ();
/*else*/ {
test t = func1 (r - 1);
t.m_strings.push_back ("lala");
return t;
}
}
Or even mutual recursion
test func1 (int r)
{
return r == 0 ? test () : private_func1_assumes_r_greater_than_zero(r);
}
test private_func1_assumes_r_greater_than_zero (int r)
{
test t = func1 (r - 1);
t.m_strings.push_back ("lala");
return t;
}
On Mon, 19 Apr 2004 10:17:55 -0700 in comp.lang.c++, "E. Robert Tisdale"
<E.**************@jpl.nasa.gov> wrote, David Harmon wrote:
I am sure your example is simplified and that there would be other concerns in a complex application. Nevertheless, for the purpose of the simple example I would consider constructing a single object and using a member function with the implied 'this'
This is *very* bad advice.
Then what is so bad about it? It works as intended. I see you made
some useless and irrelevant changes to my suggested code, but nothing of
substance. So how would you code it to squeeze the desired result out
of a compiler that does not include NRVO?
On Mon, 19 Apr 2004 21:12:58 GMT, "Siemel Naran"
<Si*********@REMOVE.att.net> wrote: "tom_usenet" <to********@hotmail.com> wrote in message news:uf********************************@4ax.com.. . On Sun, 18 Apr 2004 14:57:20 +0200, "Jakob Bieling" <ne*****@gmy.net> >test func1 (int r) >{ > if (r == 0) > return test ();
The line above disables the optimization for most compilers. Generally, for NRVO to work, all return statements must return the same object (although this isn't a requirement of 12.8/15, just common practice).
Compilers should allow NVRO if the different return statements return objects declared after the previous return statement.
They don't in general though. NRVO is generally implemented by passing
a hidden parameter for a pointer to some storage in which the return
value should be constructed, but compilers generally only bother if
there is only one object to return, since in the general case it is
very hard to do otherwise. e.g.
test f()
{
test t(5);
if (today == TUESDAY)
return test(10);
else
return t;
}
The compiler doesn't know whether to construct t in the return storage
space until runtime, so it doesn't bother, and just copies u or v into
the return value without optimizing it.
Maybe explicit braces will help, but I'm not sure:
test func1 (int r) { if (r == 0) return test ();
/*else*/ { test t = func1 (r - 1); t.m_strings.push_back ("lala"); return t; } }
That doesn't do it - the braces make no difference.
Or even mutual recursion
test func1 (int r) { return r == 0 ? test () : private_func1_assumes_r_greater_than_zero(r); }
test private_func1_assumes_r_greater_than_zero (int r) { test t = func1 (r - 1); t.m_strings.push_back ("lala"); return t; }
That does do it, since it follows the rules I posted in my previous
post (if a named local is returned, all returns return the same one).
Still, simpler is the version I posted:
test func1 (int r)
{
test t(r == 0? test() : func1 (r - 1));
if (r != 0)
t.m_strings.push_back ("lala");
return t;
}
also does the job.
Tom
--
C++ FAQ: http://www.parashift.com/c++-faq-lite/
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
"tom_usenet" <to********@hotmail.com> wrote in message
news:9a********************************@4ax.com... Still, simpler is the version I posted:
test func1 (int r) { test t(r == 0? test() : func1 (r - 1)); if (r != 0) t.m_strings.push_back ("lala"); return t; }
also does the job.
Thanks Tom, but as I am bound to using VC++ 7.1, I guess I will try to
use the proxy-method mentioned by Denis.
Thanks!
--
jb
(replace y with x if you want to reply by e-mail) This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
by: pmatos |
last post by:
Hi all,
Sometimes I have a function which creates an object and returns it.
Some are sets, other vectors but that's not very important. In these
cases I do something like this:
vector<int> *...
|
by: libs |
last post by:
I have A.exe that should catch the return code of B.exe (both are written in VB.net) so A.exe can continue processing other commands. but A.exe cannot catch B's return code so an exception is not...
|
by: Ed Jay |
last post by:
<disclaimer>js newbie</disclaimer>
My page has a form comprised of several radio buttons. I want to poll the
buttons to determine which button was selected and convert its value to a
string. I...
|
by: ctor |
last post by:
Hi,
I'm experiencing an annoying issue. Here is a simplified idea of what
I am trying to do. Inclusion guards aren't shown for readability. I
hope this isn't too confusing; I don't think a...
|
by: zl2k |
last post by:
hi, c++ user
Suppose I constructed a large array and put it in the std::vector in a
function and now I want to return it back to where the function is called.
I can do like this:
...
|
by: Eric Lilja |
last post by:
Hello, consider this complete program:
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
class Hanna
{
|
by: Axel Bock |
last post by:
Hi all,
I am trying to get my head around what happens if I return a class
object from a function.
It seems C++ (MinGW) does not invoke the copy constructor if I do
something like this:
...
|
by: Igor.Smirnov |
last post by:
Hi,
Why g++ in Linux does not call copy constructor at return statement?
I have detected this at Sicentific Linux 4.1 (g++ v. 3.4.3) and Red Hat
Linux 8.0 (g++ v. 3.2).
Microsoft Visual...
|
by: terminator(jam) |
last post by:
consider:
struct memory_pig{//a really large type:
memory_pig(){
std::cout<<"mem pig default\n";
//etc...
};
memory_pig(memory_pig const&){
|
by: taylorcarr |
last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
|
by: Charles Arthur |
last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
|
by: aa123db |
last post by:
Variable and constants
Use var or let for variables and const fror constants.
Var foo ='bar';
Let foo ='bar';const baz ='bar';
Functions
function $name$ ($parameters$) {
}
...
|
by: ryjfgjl |
last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
|
by: emmanuelkatto |
last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud.
Please let me know.
Thanks!
Emmanuel
|
by: BarryA |
last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
|
by: nemocccc |
last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
|
by: Hystou |
last post by:
There are some requirements for setting up RAID:
1. The motherboard and BIOS support RAID configuration.
2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
|
by: marktang |
last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
| |