Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I be sure a routine is taking advantage of (N)RVO?

I'd like to make sure my routines are leveraging (N)RVO whenever possible. Other than parsing through the resulting disassembly, is there something I can do or check to see if a routine is being compiled with (N)RVO? At this point I'm mostly interested in MSVC and GCC.

like image 818
fbrereto Avatar asked Mar 08 '12 17:03

fbrereto


3 Answers

No, not really.

However you can follow guidelines when writing your code.


Unnamed Return Value Optimization

This is pretty much triggered every time you return a temporary, even in Debug mode.

return MyObject(....);

Named Return Value Optimization

This is pretty much triggered every time the function always return the same local variable:

MyObject func() {
  MyObject result;
  if (...) { return result; }

  result.push(0);
  return result;
}

You can mix those, but it becomes nigh impossible for the compiler to apply RVO in this case:

MyObject func() {
  MyObject result;
  if (...) { return MyObject(...); }

  return result;
}

Here, it is probably that one return will benefit from RVO and the other will not. And I would bet on the first being optimized because you'd be stuck if you speculatively create result in the return slot and suddenly need to take the if branch. Note that simply reordering the statements just work:

MyObject func() {
  if (...) { return MyObject(...); }

  MyObject result;

  return result;
}

So the rule of thumb for NRVO is that there should be no return statement between the declaration of result and the return result; statement that return anything else than result itself.


If you follow this, you stack the odds in your favor. And then it's just a matter of code review.

And you also make your code easier to read since you do not declare variables before knowing that you really need them!

like image 169
Matthieu M. Avatar answered Oct 30 '22 19:10

Matthieu M.


You can add debug methods to the destructor:

struct A
{
   ~A() { cout << "destructor called"; }
};

A foo()
{
   A a;
   return a;
}

If the destructor is called, RVO was probably not applied.

like image 21
Luchian Grigore Avatar answered Oct 30 '22 17:10

Luchian Grigore


Possible ways I can think of are:

  1. Implementing a reference counting mechanism within your class which keeps track of the number of instances created through the class, something pretty much like a shared_ptr does, This way You can detect extra copies of your class being created and removed if copy elision is not happening.

  2. You could simply put debug traces in copy constructor and destructor for your class, if copy elision is not happening you would see lot of successive copy constructor and destructor debug traces.

like image 23
Alok Save Avatar answered Oct 30 '22 18:10

Alok Save