public inbox for gcc-help@gcc.gnu.org
 help / color / mirror / Atom feed
* how to pass params to inline functions by reference or value?
@ 2008-04-10  5:55 VM
  2008-04-10 13:32 ` Ted Byers
  2008-04-10 15:33 ` John Fine
  0 siblings, 2 replies; 9+ messages in thread
From: VM @ 2008-04-10  5:55 UTC (permalink / raw)
  To: gcc-help

Hello,

I'm trying to decide on the best way to pass parameters to inline
function. For example, the two functions below:

inline int
sum(atype_t *x)
{
  return x->a + x->b;
}

and

inline int
sum(atype_t x)
{
  return x.a + x.b;
}

Since the function is inline, my guess is that the compiler will
generate identical code for both. So there should be no performance
difference.

Is this assumption correct?

V

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: how to pass params to inline functions by reference or value?
  2008-04-10  5:55 how to pass params to inline functions by reference or value? VM
@ 2008-04-10 13:32 ` Ted Byers
  2008-04-10 13:37   ` Vincent Mayeski
  2008-04-10 15:33 ` John Fine
  1 sibling, 1 reply; 9+ messages in thread
From: Ted Byers @ 2008-04-10 13:32 UTC (permalink / raw)
  To: VM, gcc-help

--- VM <mayeski@gmail.com> wrote:
> Hello,
> 
> I'm trying to decide on the best way to pass
> parameters to inline
> function. For example, the two functions below:
> 
> inline int
> sum(atype_t *x)
> {
>   return x->a + x->b;
> }
> 
> and
> 
> inline int
> sum(atype_t x)
> {
>   return x.a + x.b;
> }
> 
> Since the function is inline, my guess is that the
> compiler will
> generate identical code for both. So there should be
> no performance
> difference.
> 
> Is this assumption correct?
> 

No!  

Ignore, for the moment, that you are merely advising
the compiler that you want the function inlined. 
Unless things have changed since last I looked, there
is no guarantee that the compiler will inline a
function you have asked to be inlined.

Your bigger problem is that you have ignored the
semantics associated with passing an argument by
reference, pointer and value.

The first version of your function passes a pointer
argument.  Always the same size, and since you don't
say the pointer, or the object to which it is
pointing, is const, any change made to the object
within the function remains after the function
finishes and is visible to the calling code.  In the
second, you pass by value, so the copy constructor
must be called.  And any change made to that object
within the function is lost when the function
completes, and is never visible to the calling code
(unless you return the object, which again will
require invoking the copy constructor).

I would recommend you base your decisions about
whether to pass by reference or by value on what the
function needs to do to, or with, the argument object,
rather than idle speculation about what the compiler
may do IF it inlines your function.  If the compiler
isn't broken, it will not change the semantics of the
function.  Therefore, if you pass an object by value,
it has to be copied so that IF your function makes
changes to it, those changes are not visible to the
calling code and cease to exist once the function
completes.  I would not expect inlining the function
to change that.  And it gets more complicated.  For
example, is there polymorphism involved, and if so,
which version of the virtual function used do you need
in the context of your function?  The bottom line is
that there is no one best way to pass arguments since
what you do depends on what is needed, irrespective of
whether or not the function you're analyzing ought to
be inlined.

HTH

Ted

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: how to pass params to inline functions by reference or value?
  2008-04-10 13:32 ` Ted Byers
@ 2008-04-10 13:37   ` Vincent Mayeski
  2008-04-10 14:45     ` Ted Byers
  2008-04-10 15:11     ` Eljay Love-Jensen
  0 siblings, 2 replies; 9+ messages in thread
From: Vincent Mayeski @ 2008-04-10 13:37 UTC (permalink / raw)
  To: Ted Byers; +Cc: gcc-help

Thanks Ted.

I see your point. I should have included the const in the first version
of the function. So we would have:

inline int
sum(const atype_t *x)
{
  return x->a + x->b;
}

and

inline int
sum(atype_t x)
{
  return x.a + x.b;
}

Now these two functions are semantically identical I believe. As you
say, the second implementation suggests that the arg needs to be copied.
However, IF the function is inlined, no copying should actually take
place because the arg is not modified within the function. My guess is
that these will produce identical code.

If the compiler generated different code for each, then the second
implementation should be faster because there is no dereferencing
needed.

Is this correct?


On Thu, 2008-04-10 at 08:20 -0400, Ted Byers wrote:
> --- VM <mayeski@gmail.com> wrote:
> > Hello,
> > 
> > I'm trying to decide on the best way to pass
> > parameters to inline
> > function. For example, the two functions below:
> > 
> > inline int
> > sum(atype_t *x)
> > {
> >   return x->a + x->b;
> > }
> > 
> > and
> > 
> > inline int
> > sum(atype_t x)
> > {
> >   return x.a + x.b;
> > }
> > 
> > Since the function is inline, my guess is that the
> > compiler will
> > generate identical code for both. So there should be
> > no performance
> > difference.
> > 
> > Is this assumption correct?
> > 
> 
> No!  
> 
> Ignore, for the moment, that you are merely advising
> the compiler that you want the function inlined. 
> Unless things have changed since last I looked, there
> is no guarantee that the compiler will inline a
> function you have asked to be inlined.
> 
> Your bigger problem is that you have ignored the
> semantics associated with passing an argument by
> reference, pointer and value.
> 
> The first version of your function passes a pointer
> argument.  Always the same size, and since you don't
> say the pointer, or the object to which it is
> pointing, is const, any change made to the object
> within the function remains after the function
> finishes and is visible to the calling code.  In the
> second, you pass by value, so the copy constructor
> must be called.  And any change made to that object
> within the function is lost when the function
> completes, and is never visible to the calling code
> (unless you return the object, which again will
> require invoking the copy constructor).
> 
> I would recommend you base your decisions about
> whether to pass by reference or by value on what the
> function needs to do to, or with, the argument object,
> rather than idle speculation about what the compiler
> may do IF it inlines your function.  If the compiler
> isn't broken, it will not change the semantics of the
> function.  Therefore, if you pass an object by value,
> it has to be copied so that IF your function makes
> changes to it, those changes are not visible to the
> calling code and cease to exist once the function
> completes.  I would not expect inlining the function
> to change that.  And it gets more complicated.  For
> example, is there polymorphism involved, and if so,
> which version of the virtual function used do you need
> in the context of your function?  The bottom line is
> that there is no one best way to pass arguments since
> what you do depends on what is needed, irrespective of
> whether or not the function you're analyzing ought to
> be inlined.
> 
> HTH
> 
> Ted

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: how to pass params to inline functions by reference or value?
  2008-04-10 13:37   ` Vincent Mayeski
@ 2008-04-10 14:45     ` Ted Byers
  2008-04-10 15:11     ` Eljay Love-Jensen
  1 sibling, 0 replies; 9+ messages in thread
From: Ted Byers @ 2008-04-10 14:45 UTC (permalink / raw)
  To: Vincent Mayeski; +Cc: gcc-help

--- Vincent Mayeski <mayeski@gmail.com> wrote:
> Thanks Ted.
> 
> I see your point. I should have included the const
> in the first version
> of the function. So we would have:
> 
> inline int
> sum(const atype_t *x)
> {
>   return x->a + x->b;
> }
> 
> and
> 
> inline int
> sum(atype_t x)
> {
>   return x.a + x.b;
> }
> 
> Now these two functions are semantically identical I
> believe. 

No, not quite.  Think about your function that takes
pointers.  What is constant?  The pointer, or the
object it points to, or both?

In the version that takes the argument by value, what
is constant?  The address of the object, the object,
or both?

Now, extend your consideration to assume that the data
members 'a' and 'b' are instances of classes that are
members of an inheritance tree, and that there are
virtual functions involved.  Which implementation of
these virtual functions SHOULD be called, and which
WILL be called?

> As you
> say, the second implementation suggests that the arg
> needs to be copied.
> However, IF the function is inlined, no copying
> should actually take
> place because the arg is not modified within the
> function. My guess is
> that these will produce identical code.
> 
How is this assumption clear from the body of the
function?  We don't know what the data members 'a' and
'b' are, or what operator+() does to them.  Yes, if
they are pods, arg is not modified, but if they are
not, almost anything goes?  And if you call functions
in your inline function, and they call other
functions, which in turn call other functions, you can
not know that the objects passed as an argument are
not modified by the function, unless you have complete
and correct specification of const through your entire
codebase.  And this raises the question of WHICH
codebase.  The code in question may well be part of a
library used in a variety of projects, and the data
members accessed in your function may be wrappers for
implementing classes, so the wrappers' member
functions may well all be inlined, but the
implementing classes may or may not guarantee that
accessor functions or operators defined for the class
guarantee that the object won't be changed.  And none
of this affects whether or not the function could or
should be inlined.

I am not sure I'd want a compiler to analyse all that
in order to figure out whether or not to copy an
argument passed by value when deciding to inline a
function.

> If the compiler generated different code for each,
> then the second
> implementation should be faster because there is no
> dereferencing
> needed.
> 
> Is this correct?
> 
No!  

Unless someone who has developed the compiler code
that handles inlining can tell us that there is a way
for the compiler to guarantee that the argument passed
by value is not changed without doing the copy, it
must be assumed that the copy is done regardless of
whether the function is inlined or not.

HTH

Ted

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: how to pass params to inline functions by reference or value?
  2008-04-10 13:37   ` Vincent Mayeski
  2008-04-10 14:45     ` Ted Byers
@ 2008-04-10 15:11     ` Eljay Love-Jensen
  2008-04-10 15:37       ` Vincent Mayeski
  1 sibling, 1 reply; 9+ messages in thread
From: Eljay Love-Jensen @ 2008-04-10 15:11 UTC (permalink / raw)
  To: Vincent Mayeski; +Cc: GCC-help

Hi Vincent,

> However, IF the function is inlined, no copying should actually take
> place because the arg is not modified within the function. My guess is
> that [#1] these will produce identical code.

Is the context C++ or C?

For C++, that [#1] is incorrect for non-regular types, and likely incorrect
for regular types.  (But for C++ you probably should use a const& for the
inline function's parm, which would do what you want.)

For C, I'm not sure, but I suspect that [#1] is incorrect.

What happens when you test compile the two variants with --save-temps and
-O2, and look at the resulting .s files?

HTH,
--Eljay

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: how to pass params to inline functions by reference or value?
  2008-04-10  5:55 how to pass params to inline functions by reference or value? VM
  2008-04-10 13:32 ` Ted Byers
@ 2008-04-10 15:33 ` John Fine
  2008-04-11 11:57   ` Brian Dessent
  1 sibling, 1 reply; 9+ messages in thread
From: John Fine @ 2008-04-10 15:33 UTC (permalink / raw)
  To: VM; +Cc: gcc-help

Your second version tells the compiler to use atype_t's copy constructor 
to make a copy of the object and then get a and b from that copy.

After earlier compiler steps generate an intermediate level 
representation of that copy constructor, the optimizer might well figure 
out that all the copying is redundant and eliminate it and generate the 
minimal code which you want.

All that assumes the compiler really does take your suggestion of 
"inline".  As someone else already mentioned, "inline" is just a 
suggestion.  If the code weren't inlined then the optimizer could not 
avoid the copy operation.

I think it is cleanest to pass by const reference, assuming the real 
code, like your reduced example, only reads the structure, and assuming 
we are talking about C++, not C.

inline int
sum(atype_t const& x)
{
  return x.a + x.b;
}

That gives the compiler the least opportunity to generate bad code.  
Maybe all versions discussed will generate equal code, but why push your 
luck.


VM wrote:

>Hello,
>
>I'm trying to decide on the best way to pass parameters to inline
>function. For example, the two functions below:
>
>inline int
>sum(atype_t *x)
>{
>  return x->a + x->b;
>}
>
>and
>
>inline int
>sum(atype_t x)
>{
>  return x.a + x.b;
>}
>
>Since the function is inline, my guess is that the compiler will
>generate identical code for both. So there should be no performance
>difference.
>
>Is this assumption correct?
>
>V
>
>
>  
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: how to pass params to inline functions by reference or value?
  2008-04-10 15:11     ` Eljay Love-Jensen
@ 2008-04-10 15:37       ` Vincent Mayeski
  2008-04-10 21:39         ` Tony Wetmore
  0 siblings, 1 reply; 9+ messages in thread
From: Vincent Mayeski @ 2008-04-10 15:37 UTC (permalink / raw)
  To: Eljay Love-Jensen; +Cc: GCC-help

Thanks everyone,

I'm looking at this purely from a performance point of view. I just want
the version that is faster. I'm assuming the context of C, and that the
function does indeed get inlined.

The .s files are indeed different. Unfortunately I don't know assembler
well enough to tell what the difference is.

I'm using the -S flag. Is there a better way to map assembler code back
to C source?


On Thu, 2008-04-10 at 08:37 -0500, Eljay Love-Jensen wrote:
> Hi Vincent,
> 
> > However, IF the function is inlined, no copying should actually take
> > place because the arg is not modified within the function. My guess is
> > that [#1] these will produce identical code.

> 
> Is the context C++ or C?
> 
> For C++, that [#1] is incorrect for non-regular types, and likely incorrect
> for regular types.  (But for C++ you probably should use a const& for the
> inline function's parm, which would do what you want.)
> 
> For C, I'm not sure, but I suspect that [#1] is incorrect.
> 
> What happens when you test compile the two variants with --save-temps and
> -O2, and look at the resulting .s files?
> 
> HTH,
> --Eljay
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: how to pass params to inline functions by reference or value?
  2008-04-10 15:37       ` Vincent Mayeski
@ 2008-04-10 21:39         ` Tony Wetmore
  0 siblings, 0 replies; 9+ messages in thread
From: Tony Wetmore @ 2008-04-10 21:39 UTC (permalink / raw)
  To: Vincent Mayeski; +Cc: GCC-help

Vincent Mayeski wrote:
 > I'm using the -S flag. Is there a better way to map assembler
 > code back to C source?

Vincent,

I have used the following command-line in the past to generate assembly 
alisting files (with integrated source):

   gcc -c -g -O3 -Wa,-adhls=func.list func.c

As part of the assembly process, the "func.list" file is generated, 
containing the source and assembly, like so:

    1                            .file   "func.c"
    4                            .text
    5                    Ltext0:
   28                            .p2align 4,,15
   32                    .globl _func
   34                    _func:
    1:func.c        **** float func( float a, float b )
    2:func.c        **** {
   36                    LM1:
   37 0000 55                    pushl   %ebp
   38 0001 89E5                  movl    %esp, %ebp
   40                    LM2:
   41 0003 D9450C                flds    12(%ebp)
    3:func.c        ****   return a * b;
   43                    LM3:
   44 0006 D84D08                fmuls   8(%ebp)
    4:func.c        **** }
   46                    LM4:
   47 0009 5D                    popl    %ebp
   48 000a C3                    ret
   50                    Lscope0:
   52                            .text
   54 000b 90909090      Letext:
   54      90

It is not the easiest thing in the world to read, but it might help.

Another alternative would be to step through the code in a debugger that 
can show the source and assembly code together, such as the Insight 
graphical debugger front end for gdb.

--
Tony Wetmore


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: how to pass params to inline functions by reference or value?
  2008-04-10 15:33 ` John Fine
@ 2008-04-11 11:57   ` Brian Dessent
  0 siblings, 0 replies; 9+ messages in thread
From: Brian Dessent @ 2008-04-11 11:57 UTC (permalink / raw)
  To: John Fine; +Cc: VM, gcc-help

John Fine wrote:

> All that assumes the compiler really does take your suggestion of
> "inline".  As someone else already mentioned, "inline" is just a
> suggestion.  If the code weren't inlined then the optimizer could not
> avoid the copy operation.

That is what "static inline" or "__attribute__((always_inline))" is for.

Tony Wetmore wrote:

> I have used the following command-line in the past to generate assembly
> alisting files (with integrated source):
> 
>    gcc -c -g -O3 -Wa,-adhls=func.list func.c
> 
> As part of the assembly process, the "func.list" file is generated,
> containing the source and assembly, like so:

That is one way, you can also use objdump:

objdump -drwS func.o

This of course requires that func.o was compiled with debug info (-g),
otherwise there's no source line information.  Note that when looking at
the disassembly output of an unlinked object it's easy to get confused
if you don't pay attention to relocs, as e.g. jump targets and function
calls are all encoded as zero in the opcode, as the linker has not run
yet.  But the actual symbolic location is encoded in the reloc, and with
-r objdump should make this clear enough.

Brian

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2008-04-10 21:46 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-04-10  5:55 how to pass params to inline functions by reference or value? VM
2008-04-10 13:32 ` Ted Byers
2008-04-10 13:37   ` Vincent Mayeski
2008-04-10 14:45     ` Ted Byers
2008-04-10 15:11     ` Eljay Love-Jensen
2008-04-10 15:37       ` Vincent Mayeski
2008-04-10 21:39         ` Tony Wetmore
2008-04-10 15:33 ` John Fine
2008-04-11 11:57   ` Brian Dessent

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).