Topic: Inlining and copy-elision


Author: oporat@yahoo.com (Ofer Porat)
Date: Mon, 25 Jun 2007 13:55:51 GMT
Raw View
Consider the code

=====
struct T {};

inline void g(T const& p)
{
    T q(p);
}

int main()
{
    g(T());
}
=====

If the function g() is not inlined, the copy construction of q may not
be elided, because the temporary passed to g() is bound to a reference
(p).

What happens if g() is inlined?  Since p is only used once in g(), the
compiler might eliminate the p parameter altogether.  Is the compiler
allowed to elide the copy construction of q and instead
value-initialize q directly?




____________________________________________________________________________________
Need Mail bonding?
Go to the Yahoo! Mail Q&A for great tips from Yahoo! Answers users.
http://answers.yahoo.com/dir/?link=list&sid=396546091

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Mon, 25 Jun 2007 16:51:36 GMT
Raw View
Ofer Porat ha scritto:
> Consider the code
>
> =====
> struct T {};
>
> inline void g(T const& p)
> {
>     T q(p);
> }
>
> int main()
> {
>     g(T());
> }
> =====
>
> If the function g() is not inlined, the copy construction of q may not
> be elided, because the temporary passed to g() is bound to a reference
> (p).
>
> What happens if g() is inlined?  Since p is only used once in g(), the
> compiler might eliminate the p parameter altogether.  Is the compiler
> allowed to elide the copy construction of q and instead
> value-initialize q directly?
>

According to my interpretation, in this specific case the answer is yes.
Notice that the criteria set in 12.8/15 are about omitting the copy
"even if the copy constructor and/or destructor for the object have side
effects." If the copy ctor and/or dtor do *not* have side-effect, as it
happens in this case, then the presence of absence of the copy is not
part of the "observable behavior" so the compiler can practically do
whatever it want: it can elide the copy or not, the program can't tell
the difference anyway (timed benchmarks unfortunately do not count as
"observations"). Of course the semantic (accessibility, etc.) must still
be respected, but that't another story.

HTH,

Ganesh

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: oporat@yahoo.com ("Ofer Porat")
Date: Mon, 25 Jun 2007 17:41:56 GMT
Raw View
"Alberto Ganesh Barbati" <AlbertoBarbati@libero.it> wrote
| According to my interpretation, in this specific case the answer is
yes.

Sorry, I didn't mean for the question to revolve around the actual
content of the copy constructor.
Say T is defined as

struct T { T(T const&); };

so the compiler doesn't know what's in the copy constructor (it's in
another translation unit).

Section [class.temporary] says that if the creation of a temporary is
avoided, "all semantic restrictions must be respected."  What does that
mean?  Does that mean that binding the reference parameter to the
temporary "must be respected" even if g() is inlined?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Mon, 25 Jun 2007 21:34:23 GMT
Raw View
Ofer Porat ha scritto:
> "Alberto Ganesh Barbati" <AlbertoBarbati@libero.it> wrote
> | According to my interpretation, in this specific case the answer is
> yes.
>
> Sorry, I didn't mean for the question to revolve around the actual
> content of the copy constructor.

But the actual content of the copy constructor is relevant to question
anyway. The standard defines the output of a program only in terms of
its observable behaviour. If neither the copy constructor nor the
destructor have side-effects, the mere presence of the copy is not
observable so it doesn't make sense to discuss whether it occurs or not.

> Say T is defined as
>
> struct T { T(T const&); };
>
> so the compiler doesn't know what's in the copy constructor (it's in
> another translation unit).

The fact that the copy ctor is in another translation doesn't matter, at
least in theory. In fact the standard never says that crossing
translation unit boundaries shall interfere with this kind of
optimizations. In practice, it may have mattered some time ago when most
implementation where based on the legacy C compiler-linker architecture.
More recently smarter linkers and link-time code generation came up, so
nowadays there are implementation that can actually elide the copy even
across translation unit boundaries.

> Section [class.temporary] says that if the creation of a temporary is
> avoided, "all semantic restrictions must be respected."  What does that
> mean?  Does that mean that binding the reference parameter to the
> temporary "must be respected" even if g() is inlined?

The sentence is about "semantic restrictions" and the following note is
quite explanatory: "even if the copy constructor is not called, all the
semantic restrictions, such as accessibility (clause 11), shall be
satisfied." Binding is not a "semantic restriction" so the sentence
doesn't refer to the necessity of performing it or not, IMHO.

HTH,

Ganesh

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Francis Glassborow <francis.glassborow@btinternet.com>
Date: Tue, 26 Jun 2007 09:48:09 CST
Raw View
Ofer Porat wrote:
> "Alberto Ganesh Barbati" <AlbertoBarbati@libero.it> wrote
> | According to my interpretation, in this specific case the answer is
> yes.
>
> Sorry, I didn't mean for the question to revolve around the actual
> content of the copy constructor.
> Say T is defined as
>
> struct T { T(T const&); };
>
> so the compiler doesn't know what's in the copy constructor (it's in
> another translation unit).

Yes it does. Any constructor that has its first explicit parameter a cv
qualified reference and all (if any) other parameters have default
arguments IS a copy ctor. The code definition of the ctor is irrelevant,
the compiler is allowed to assume (even in the face of evidence to the
contrary, such as a visible definition) that such a ctor when used to
copy does exactly that and no more. Even if the ctor and/or dtor have
side effects the copy can still be optimised away (or not as the case
may be) without any regard to the definitions.



--
Please do NOT use robinton.demon.co.uk addresses
They cease to be valid on July 14
replace with Francis.Glassborow at btinternet.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Ofer Porat" <oporat@yahoo.com>
Date: Tue, 26 Jun 2007 12:47:58 CST
Raw View
----- Original Message -----
From: "Alberto Ganesh Barbati" <AlbertoBarbati@libero.it>
Newsgroups: comp.std.c++
| But the actual content of the copy constructor is relevant to question
| anyway.

It's difficult to tell whether you really don't understand my question
or
are just pretending not to.  The compiler is given the following
complete
and compilable source file

==========
struct T
{
    T()
    {
    }

    T (T const&);
};

inline void g(T const& p)
{
    T q(p);
}

int main()
{
    g(T());
    return 0;
}
==========

The compiler is not given any other source file.  No link takes place.
The other code
will be written, compiled and linked separately some time in the future.
The compiler
still has to generate an object file from this isolated source file.
When generating
code for *this* compilation, the compiler must decide whether to
generate a call
to the copy constructor or not.  Since the compiler doesn't know
anything about the
copy constructor, it must conservatively assume that eliding the copy
*does* alter
observable behavior.  If the copy constructor does alter observable
behavior, the only
rules under which the compiler is permitted to elide the copy is section
[class.copy]
clause 15.  The relevant rule for this code is the one that says that a
copy of a
temporary may be elided if the temporary is not bound to a reference,
and as long as
"semantic restrictions are respected".  This is where the inlining issue
comes in.  The
standard says nothing about what kind of transformations are allowed
during inlining.
It doesn't even say that inlining must not change program behavior (at
least not that I
could find, maybe I missed it.)

Let me explain why I asked this question in the first place.  I was
thinking about

std::vector<T>::push_back(T const&);

Say you write code like this

std::vector<T> v;
v.reserve(10);
v.push_back(T(/* some valid construction of T */));

What usually happens during the 3rd statement is that a temporary T is
created and then
copied (with placement new) into the new position in the vector.  I was
wondering whether
the compiler could ever elide this copy (based on the copy elision
rules).  Since vector
member functions are usually inlined (maybe at multiple depth, but
compilers can do that),
it boils down to the question I asked.

The latest draft contains a version of push_back that's based on move
constructors

std::vector<T>::push_back(T&&);

so this could provide a performance enhancement.  It's going to be some
time until
compilers support this, so I was just thinking whether compiler can
optimize push_back
even with the existing version.  I couldn't find a compiler that elides
the copy in push_back.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Tue, 26 Jun 2007 21:19:38 GMT
Raw View
Ofer Porat ha scritto:
> ----- Original Message -----
> From: "Alberto Ganesh Barbati" <AlbertoBarbati@libero.it>
> Newsgroups: comp.std.c++
> | But the actual content of the copy constructor is relevant to question
> | anyway.
>
> It's difficult to tell whether you really don't understand my question
> or
> are just pretending not to.

Please don't be rude. I think I have well understood your question,
however you keep seeing the problem in the wrong way and that prevents
you from understanding my answer.

> are just pretending not to.  The compiler is given the following
> complete
> and compilable source file
>
> ==========
> struct T
> {
>     T()
>     {
>     }
>
>     T (T const&);
> };
>
> inline void g(T const& p)
> {
>     T q(p);
> }
>
> int main()
> {
>     g(T());
>     return 0;
> }
> ==========
>
> The compiler is not given any other source file.  No link takes place.
> The other code
> will be written, compiled and linked separately some time in the future.
> The compiler
> still has to generate an object file from this isolated source file.
> When generating
> code for *this* compilation, the compiler must decide whether to
> generate a call
> to the copy constructor or not.

No. It doesn't. That is needed with the legacy compiler/linker approach,
but the standard does *not* mandate such approach. In fact, smarter and
more recent compilers can produce output that is somehow "richer" than
plain old "object code" and this *allows* deferring the decision at a
later time, during link-time code generation, which is a much more
complex step than legacy linking. It doesn't matter if linking occurs
immediately or the next year.

> observable behavior.  If the copy constructor does alter observable
> behavior, the only
> rules under which the compiler is permitted to elide the copy is section
> [class.copy]
> clause 15.  The relevant rule for this code is the one that says that a
> copy of a
> temporary may be elided if the temporary is not bound to a reference,
> and as long as
> "semantic restrictions are respected".  This is where the inlining issue
> comes in.  The
> standard says nothing about what kind of transformations are allowed
> during inlining.
> It doesn't even say that inlining must not change program behavior (at
> least not that I
> could find, maybe I missed it.)

Apart from the fact that an "inline function shall be defined in every
translation unit in which it is used" ([dcl.fct.spec] para 4) and a few
other technicalities, the inline specifier shall not alter the behaviour
of the program in any way. The standard need not specify that explicitly
(it would need to be explicit if it were the other way around).

> Let me explain why I asked this question in the first place.  I was
> thinking about
>
> std::vector<T>::push_back(T const&);
>
> Say you write code like this
>
> std::vector<T> v;
> v.reserve(10);
> v.push_back(T(/* some valid construction of T */));
>
> What usually happens during the 3rd statement is that a temporary T is
> created and then
> copied (with placement new) into the new position in the vector.  I was
> wondering whether
> the compiler could ever elide this copy (based on the copy elision
> rules).  Since vector
> member functions are usually inlined (maybe at multiple depth, but
> compilers can do that),
> it boils down to the question I asked.

In presence of side effects the copy can't be omitted. In absence of
those side effects, compilers *have* some latitude. A legacy compiler
will probably not omit it, but you must understand that it's a quality
of implementation issue: the standard does not specify what must be
done, because it's not part of the observable behaviour.

> The latest draft contains a version of push_back that's based on move
> constructors
>
> std::vector<T>::push_back(T&&);
>
> so this could provide a performance enhancement.  It's going to be some
> time until
> compilers support this, so I was just thinking whether compiler can
> optimize push_back
> even with the existing version.  I couldn't find a compiler that elides
> the copy in push_back.

Have you tried a compiler with link-time code generation? Be aware that
you must inspect the final executable to actually detect the presence of
the copy. Inspecting the object code is not enough for those beasts.

Regards,

Ganesh

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Ofer Porat" <oporat@yahoo.com>
Date: Tue, 26 Jun 2007 17:00:00 CST
Raw View
----- Original Message -----
From: "Alberto Ganesh Barbati" <AlbertoBarbati@libero.it>
Newsgroups: comp.std.c++

| Apart from the fact that an "inline function shall be defined in every
| translation unit in which it is used" ([dcl.fct.spec] para 4) and a
few
| other technicalities, the inline specifier shall not alter the
behaviour
| of the program in any way. The standard need not specify that
explicitly
| (it would need to be explicit if it were the other way around).
|
| In presence of side effects the copy can't be omitted.

Thank you.  That's what I was trying to find out.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]