* 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 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 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: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).