Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a virtual function be a candidate to RVO (return value optimization)?

Are C++ compilers able to apply RVO for virtual functions?

In this case:

class AbstractReader
{
//...
public:
    virtual std::vector<float> getFloatVector() = 0;
//...
}

class XmlReader : public AbstractReader
{
//...
public:
    virtual std::vector<float> getFloatVector()
    {
        std::vector<float> result;

        //Do some parsing here...

        return result;
    }
//...
}



class BinaryReader : public AbstractReader
{
//...
public:
    virtual std::vector<float> getFloatVector()
    {
        std::vector<float> result;

        //Do some decoding here...

        return result;
    }
//...
}

Can RVO apply to return result; lines? I would guess not.

Then, is std::move(result) the way to go for returning large containers in that case?

Thanks

like image 585
galinette Avatar asked Oct 09 '14 13:10

galinette


People also ask

What is RVO and NRVO?

One form of copy elision is known as "named return value optimization" - a.k.a NRVO. Another one is known as "return value optimization" - a.k.a RVO. There are also other forms of copy elision which don't involve returned values.

Are virtual functions bad for performance?

Virtual functions are slow when you have a cache miss looking them up. As we'll see through benchmarks, they can be very slow. They can also be very fast when used carefully — to the point where it's impossible to measure the overhead.

How does return value optimization work?

In the context of the C++ programming language, return value optimization (RVO) is a compiler optimization that involves eliminating the temporary object created to hold a function's return value. RVO is allowed to change the observable behaviour of the resulting program by the C++ standard.


2 Answers

Yes, the compiler can perform RVO. I cooked up some testing code and ran it through godbolt:

struct M {
  M();
  M(const M&);
  M(M &&);
  ~M();
  double * ptr;
};

M getM();

struct A {
  virtual M foo() = 0;
};

struct B : A {
  virtual M foo() override;
};

M B::foo(){
  M m;
  return m;
}

struct C : B {
  virtual M foo() override;
};
M C::foo(){
  M m = getM();
  return m;
}

A* getA();

int main(){
  A* p = getA();
  M m = p->foo();
}

g++ -O3 produces

B::foo():
    pushq   %rbx
    movq    %rdi, %rbx
    call    M::M()
    movq    %rbx, %rax
    popq    %rbx
    ret
C::foo():
    pushq   %rbx
    movq    %rdi, %rbx
    call    getM()
    movq    %rbx, %rax
    popq    %rbx
    ret
main:
    subq    $24, %rsp
    call    getA()
    movq    (%rax), %rdx
    movq    %rax, %rsi
    movq    %rsp, %rdi
    call    *(%rdx)
    movq    %rsp, %rdi
    call    M::~M()
    xorl    %eax, %eax
    addq    $24, %rsp
    ret

Conspicuously absent from the disassembly is any call to the copy or move constructor of M.


Also, the paragraph of the standard setting out the criteria for copy elision draws no distinction between virtual and nonvirtual member functions, and whenever the standard for copy elision is met, overload resolution for the return statement "is first performed as if the object were designated by an rvalue".

That is to say, in a function

M foo() {
    M m = /*...*/;
    return m;
}

If copy elision can't take place for whatever reason, and a move constructor is available, return m; will always invoke the move constructor rather than the copy constructor. Hence, there's no need to use std::move for the return statement if you are returning a local variable.

like image 196
T.C. Avatar answered Oct 12 '22 19:10

T.C.


If you return std::move(result);, you cannot gain anything, and you can lose. So don't do it.

You cannot gain anything, because the standard explicitly says "if RVO conditions are met, or you're returning a parameter, try returning as rvalue first and only if that wouldn't compile, return as lvalue. So even if you return result;, the compiler is forced to try return std::move(result); first.

You can lose, since return std::move(result); specifically prevents RVO if it was otherwise applicable.

like image 31
Angew is no longer proud of SO Avatar answered Oct 12 '22 20:10

Angew is no longer proud of SO