Topic: Standards for inline function in initializor?


Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sat, 11 Jun 1994 16:59:46 GMT
Raw View
In article <CqqrKx.J5n@ses.com> jamshid@ses.com (Jamshid Afshar) writes:
>user-defined constructor or an array) is well-defined.  Anyone know
>whether the following will be legal ANSI/ISO C++?  The ARM does
>require that the constructors for elements in an array be called in
>order.
>
> int g=0;
> int a[] = { g++, g++, g++ };  // is there a sequence point here?
>
> int f() {
>    static int i = 0;
>    return i++;
> }
> int b[] = { f(), f(), f() };  // guaranteed b=={0,1,2} ?
>
>I don't think C allowed any initializations involving non-constant
>expressions, so this is a new problem with C++.

 My guess is that for b[] above the Standard will
require b[] to contain the values 0, 1, and 2 -- but not
necessarily in that order.

 However, there is a peculiar problem with aggregates:
some components of an aggregate initialiser may be 'constant-expressions'
and others 'dynamic' and its not clear if the whole aggregate
is initialised dynamically or half in one initialisation phase
and half in another.

 Personally I'd like to kill the stupid two phase
initialisation idea off completely and make all initialisations
dynamic.  I'm uncertain if, in conjunction with N0440,
which proposes a 'require x' clause to control initialisation
order, this will break any reasonable code not using initialisation
order tricks (which it is desirable to break).

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: jason@cygnus.com (Jason Merrill)
Date: Sun, 12 Jun 1994 01:37:32 GMT
Raw View
>>>>> John Max Skaller <maxtal@physics.su.OZ.AU> writes:

>  Personally I'd like to kill the stupid two phase
> initialisation idea off completely and make all initialisations
> dynamic.

If you did that, compiler vendors would be able to avoid changing their
code at all by pleading the as-if rule, wouldn't they?  I suppose that
two-phase initialization is an explicit instantiation of the as-if rule,
and could be left out for the sake of simplicity...

Jason





Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sun, 12 Jun 1994 18:16:15 GMT
Raw View
In article <JASON.94Jun11183732@deneb.cygnus.com> jason@cygnus.com (Jason Merrill) writes:
>>>>>> John Max Skaller <maxtal@physics.su.OZ.AU> writes:
>
>>  Personally I'd like to kill the stupid two phase
>> initialisation idea off completely and make all initialisations
>> dynamic.
>
>If you did that, compiler vendors would be able to avoid changing their
>code at all by pleading the as-if rule, wouldn't they?  I suppose that
>two-phase initialization is an explicit instantiation of the as-if rule,
>and could be left out for the sake of simplicity...

 Its possible to 'read' the value of a dynamically
initialised variable in phase 1 or 2 and get 0, and later 'read'
the same variable and get some other value, say 1.

 Under a one phase N0440, that is impossible,
such code would be ill-formed and result in a diagnostic being issued.
So its not quite 'as if'.

 Its also possible for the value of a variable to change
many times (dynamically) before initialisation is complete,
indeed there is no requirement that main() not begin execution
before initialisation has completed.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: jimad@microsoft.com (Jim Adcock)
Date: Mon, 6 Jun 1994 17:40:37 GMT
Raw View
In article <CqqrKx.J5n@ses.com> jamshid@ses.com (Jamshid Afshar) writes:
|good at optimizing inlines.  I think MS optimizes the following
|initialization of g to "int g = 120;" at compile-time.
|
| int factorial( int i )
| { if (i==0) return 1;
|   else return i*factorial(i-1); }
|
| int g = factorial(5);

Indeed the MS compilers can do this if you specify agressive optimization
and tell the compiler how many times deep you want the compiler to
consider expanding recursive functions.  Its not on by default.





Author: jamshid@ses.com (Jamshid Afshar)
Date: Wed, 1 Jun 1994 23:22:09 GMT
Raw View
In article <BWH.94May25202143@beach.cis.ufl.edu>,
Brian Hook <bwh@beach.cis.ufl.edu> wrote:
>[...]
>#define convert( a ) ( a * 8 )
>int i = convert( 8 );
>
>is usually converted to:
>
>int i = 64;
>
>at compile time by the compilers I use.  However:
>
>inlint int convert( int b ) { return b * 8; }
>
>Often is not.  And if functions become even a bit more complex than the
>above, they will usually become evaluated at run-time.
>
>Point?  This can actually cause performance problems:
>
>void some_fnc_called_alot( void )
>{
>   int array[] = {
>      convert( 1 ),
>      convert( 2 ),
>      convert( 3 ),
>      convert( 4 )
>   };
>
>   // do stuff on the array
>}
>
>Since the implementation is not guaranteed, it's conceivable that the
>compiler will expand the code inline and execute the conversion routine
>every single time the function is called.  This adds up, obviously.

Yes, it adds up, but so does the time spent executing the code in "do
stuff on the array".  Unless the rest of the function is small,
speeding up the initialization of array will not have a significant
effect on the time spent in the function.  Definitely verify that your
compiler does not optimize the code itself, and profile before using
the macro instead of the inline function.

The major disadvantage of making "convert" a macro instead of an
inline function is that the identifier "convert" then cannot be used
anywhere else (e.g., as a member function to an unrelated class).
Also, if the body of convert is actually a bit more complex, like
referencing the parameter twice, you would have to make sure you never
call convert with an expression involving side-effects:

 #define convert(b) (b*b*2)
 int g;
 int array[] = {
    convert( getDefault() ),  // woops: function called twice
    convert( ++g )            // woops: undefined behavior
 };

>Point being?  This seems to be indicative of a situation where a #define is
>going to be more desirable in the real world where compilers are not
>bulletproof.  If someone can correct me on this, I'd like them to.  As I
>stated, I've been wrestling with this one for a while.

First, remember that as far as the language is concerned the only
difference between:

 int g = 8*2;
and
 inline int Double(int i) { return i*2; }
 int g = Double(8);

is that the expression "8*2" is a "constant-expression" while the call
to Double() is not.  In C++ the only advantage of constant expressions
over other expressions is that a constant expression may be used to
specify array dimensions, case statement values, bit-field lengths,
and enumerator initializors.  A constant expression may still be
evalatued at run-time.  For that matter, multiplication may be
implemented using a loop with addition.  I don't think C++ even
requires static initializations involving constant expressions be done
at "load time" (though all statics are initialized to 0 at load time).
So, much of this discussion is strictly a quality of implementation
issue.  But bear in mind that some popular implementations are very
good at optimizing inlines.  I think MS optimizes the following
initialization of g to "int g = 120;" at compile-time.

 int factorial( int i )
 { if (i==0) return 1;
   else return i*factorial(i-1); }

 int g = factorial(5);

What *isn't* a quality of implementation issue is whether the
evaluation order of initializers to an aggregate (a struct without a
user-defined constructor or an array) is well-defined.  Anyone know
whether the following will be legal ANSI/ISO C++?  The ARM does
require that the constructors for elements in an array be called in
order.

 int g=0;
 int a[] = { g++, g++, g++ };  // is there a sequence point here?

 int f() {
    static int i = 0;
    return i++;
 }
 int b[] = { f(), f(), f() };  // guaranteed b=={0,1,2} ?

I don't think C allowed any initializations involving non-constant
expressions, so this is a new problem with C++.

Jamshid Afshar
jamshid@ses.com




Author: bwh@beach.cis.ufl.edu (Brian Hook)
Date: 02 Jun 1994 12:10:14 GMT
Raw View
In article <CqqrKx.J5n@ses.com> jamshid@ses.com (Jamshid Afshar) writes:

   In article <BWH.94May25202143@beach.cis.ufl.edu>,
   Brian Hook <bwh@beach.cis.ufl.edu> wrote:
   >[...]
   >#define convert( a ) ( a * 8 )
   >int i = convert( 8 );
   >
   >is usually converted to:
   >
   >int i = 64;
   >
   >at compile time by the compilers I use.  However:
   >
   >inlint int convert( int b ) { return b * 8; }
   >
   >Often is not.  And if functions become even a bit more complex than the
   >above, they will usually become evaluated at run-time.
   >
   >Point?  This can actually cause performance problems:
   >
   >void some_fnc_called_alot( void )
   >{
   >   int array[] = {
   >      convert( 1 ),
   >      convert( 2 ),
   >      convert( 3 ),
   >      convert( 4 )
   >   };
   >
   >   // do stuff on the array
   >}
   >
   >Since the implementation is not guaranteed, it's conceivable that the
   >compiler will expand the code inline and execute the conversion routine
   >every single time the function is called.  This adds up, obviously.

>   Yes, it adds up, but so does the time spent executing the code in "do
>   stuff on the array".  Unless the rest of the function is small,
>   speeding up the initialization of array will not have a significant
>   effect on the time spent in the function.  Definitely verify that your
>   compiler does not optimize the code itself, and profile before using
>   the macro instead of the inline function.

Often times these conversions are NOT trivial.  For example, if the above
conversion code executes a floating point multiply, and your compiler
doesn't optimize out the results, this may prove expensive on
less-than-RISC machines (i.e. my entire target audience).  The "do stuff on
the array" may often times be extremely trivial, or only slightly slower
than the conversion routines if they are executed every function call.  It
does, in fact, add up, which is why I posted.

>   The major disadvantage of making "convert" a macro instead of an
>   inline function is that the identifier "convert" then cannot be used
>   anywhere else (e.g., as a member function to an unrelated class).

This is understood and well known.

>   First, remember that as far as the language is concerned the only
>   difference between:

>    int g = 8*2;
>   and
>    inline int Double(int i) { return i*2; }
>    int g = Double(8);

>   is that the expression "8*2" is a "constant-expression" while the call
>   to Double() is not.  In C++ the only advantage of constant expressions
>   over other expressions is that a constant expression may be used to
>   specify array dimensions, case statement values, bit-field lengths,
>   and enumerator initializors.  A constant expression may still be
>   evalatued at run-time.  For that matter, multiplication may be
>   implemented using a loop with addition.  I don't think C++ even
>   requires static initializations involving constant expressions be done
>   at "load time" (though all statics are initialized to 0 at load time).
>   So, much of this discussion is strictly a quality of implementation
>   issue.

Righto.  I wasn't sure if this was an implementation or issue or not since
D&E, Stroustrup 2nd, and ARM had nothing on the topic.  Also, please note
that 4 separate compilers gave different results....this was a great worry
to me.

Also, it seems that some compilers can take the #define route as a very
strong hint to do a conversion at compile time, but defer inline functions
for run-time expansion (although this isn't always the case).  This are
significant real-world sn't always the case).  This are
significant real-world problems with me, where I have to be portable to at
least four different C++ compilers, and I cannot have my code breaking on
two of them and running too slow on the other.

>   But bear in mind that some popular implementations are very
>   good at optimizing inlines.  I think MS optimizes the following
>   initialization of g to "int g = 120;" at compile-time.

>    int factorial( int i )
>    { if (i==0) return 1;
>      else return i*factorial(i-1); }

>    int g = factorial(5);

If this is true...well, I'm impressed as hell.

Brian
--
+-----------------------------------------------------------------+
| Brian Hook            | Specializing in real-time 3D graphics   |
| Box 90315             |-----------------------------------------|
| Gainesville, FL 32607 | Internet: bwh@cis.ufl.edu | Free Tibet! |
+-----------------------------------------------------------------+




Author: Duncan@rcp.co.uk (Duncan Booth)
Date: Fri, 03 Jun 1994 11:17:09 BST
Raw View
In article <BWH.94Jun2081014@beach.cis.ufl.edu>, Brian Hook wrote:
> In article <CqqrKx.J5n@ses.com> jamshid@ses.com (Jamshid Afshar) writes:
> >   But bear in mind that some popular implementations are very
> >   good at optimizing inlines.  I think MS optimizes the following
> >   initialization of g to "int g = 120;" at compile-time.
>
> >    int factorial( int i )
> >    { if (i==0) return 1;
> >      else return i*factorial(i-1); }
>
> >    int g = factorial(5);
>
> If this is true...well, I'm impressed as hell.
>
> Brian

Yes it is true, but only if you persuade the compiler really nicely. You
need to use the /Ob2 command line option (automatically inline
functions even if they aren't explicitly declared inline), AND you
need to use:
 #pragma inline_recursion(on)
to allow the compiler to inline recursive functions. In fact that
pragma has to be on both for the factorial function and for the
variable initialisation. Turning it off at either point prevents the
function being fully expanded.

The default setting of the inline_depth pragma is sufficient to
optimise up to factorial(8), but you can set this higher (up to 255
levels of inline expansion).

The problem is that as you do this your code size gets markedly
larger, in particular the code generated for the factorial function
itself is also expanded 8 levels deep, though it would get thrown
away by the linker if you didn't actually call it elsewhere.

Interestingly although the initialisation is optimised down to:
 mov WORD PTR ?g@@3HA,120
the compiler can't go that extra step and just use a static compile
time initialiser. I wonder if there is a good reason for this?

Duncan Booth
RCP Ltd.




Author: bwh@beach.cis.ufl.edu (Brian Hook)
Date: 25 May 1994 09:10:06 GMT
Raw View

I've posted this on comp.lang.c++ several times, but I guess no one knows
the answer (or possibly this is more standards related -- likely) or cares
to answer, so I figure I'd post here now.

given the following code:

inline int foo( int a )
{
   return a+1;
}

int array[3] = {
   foo( 0 ),
   foo( 1 ),
   foo( 2 )
};


int main( void )
{

   for ( int i = 0; i < 3; i++ )
      printf( "%d\n", array[i] );

   return 0;
}

Bill Davidson at Cray mentioned that he tried this on 3 separate compilers
and that they all produced different results (one didn't compile).  Borland
C++ 3.1 seems to produce different code depending on God knows what -- I've
had it evaluate some stuff at compile time, other things at startup, and
yet other things during execution of the relevant blocks of code.

I've looked in the ARM, Stroustrup 2nd, and D&E, and I've found nothing on
the subject.  I would really like to get this cleared up because this seems
to be one of those areas where a #define is going to much more efficient
(since many compilers take #defines and evaluate them at compile time).  I
have a conversion function:

inline int fixtoint( fix_t a )
{
   return a >> 16;
}

That is called a lot.  Under BC++ 3.1 I used #defines in the original
version of my code, and whenever it found something like:

   int i = fixtoint( 0x10000 );

It would compile to:

   int i = 1;

Whereas under some conditions using inline functions it generates the
equivalent assembly for the actual shift!

Thanks,

Brian




Author: svv@phoenix.dev.macsch.com (Suresh Vaidyanathan)
Date: Wed, 25 May 1994 19:06:11 GMT
Raw View
In article <2rv4le$2dm@sand.cis.ufl.edu> bwh@beach.cis.ufl.edu (Brian Hook) writes:
>
>
>I've posted this on comp.lang.c++ several times, but I guess no one knows
>the answer (or possibly this is more standards related -- likely) or cares
>to answer, so I figure I'd post here now.
>
>given the following code:
>
>inline int foo( int a )
>{
>   return a+1;
>}
>
>int array[3] = {
>   foo( 0 ),
>   foo( 1 ),
>   foo( 2 )
>};
>
>
>int main( void )
>{
>
>   for ( int i = 0; i < 3; i++ )
>      printf( "%d\n", array[i] );
>
>   return 0;
>}
>

Okay, Okay... You have nagged us enough, so I will give you my 2c on this :-)



Author: bwh@beach.cis.ufl.edu (Brian Hook)
Date: 26 May 1994 00:21:42 GMT
Raw View
In article <CqDH2C.KwD@draco.macsch.com> svv@phoenix.dev.macsch.com (Suresh Vaidyanathan) writes:

> Okay, Okay... You have nagged us enough, so I will give you my 2c on this :-)
> From the reading on Initializers(r.8.4) from Stroustrup 2e where it says

>  "Automatic, register, static and external variables may be initialized by
>  arbitrary expressions involving constants and previously declared variables
>  and functions."

This seems straightforward enough, however as you stated later, whether
this is evaluated at run time vs. compile time is dependent on the
intelligence of the compiler.

This seems to indicate to me that using a #define for basic conversions is
actually more desirable than an inline function since #defines are fairly
easy for a compiler to evaluate if they are using a constant expression:

#define convert( a ) ( a * 8 )

int i = convert( 8 );

is usually converted to:

int i = 64;

at compile time by the compilers I use.  However:

inlint int convert( int b ) { return b * 8; }

Often is not.  And if functions become even a bit more complex than the
above, they will usually become evaluated at run-time.

Point?  This can actually cause performance problems:

void some_fnc_called_alot( void )
{
int array[] = {
   convert( 1 ),
   convert( 2 ),
   convert( 3 ),
   convert( 4 )
};

   // do stuff on the array
}

Since the implementation is not guaranteed, it's conceivable that the
compiler will expand the code inline and execute the conversion routine
every single time the function is called.  This adds up, obviously.

Point being?  This seems to be indicative of a situation where a #define is
going to be more desirable in the real world where compilers are not
bulletproof.  If someone can correct me on this, I'd like them to.  As I
stated, I've been wrestling with this one for a while.

>   I think that it should work and give you the same results on all
>compilers.

_should_ being the operative word.  After messing around with several
different compilers, I was thoroughly confused on the subject (given that
one didn't compile at all, and three gave completely different results),
and thus this post.

Thanks

Brian

--
+-----------------------------------------------------------------+
| Brian Hook            | Specializing in real-time 3D graphics   |
| Box 90315             |-----------------------------------------|
| Gainesville, FL 32607 | Internet: bwh@cis.ufl.edu | Free Tibet! |
+-----------------------------------------------------------------+




Author: p150651@cc.tut.fi (Pulkkinen Esa)
Date: 26 May 94 14:30:19
Raw View
>#define convert( a ) ( a * 8 )
>
>int i = convert( 8 );
>
>is usually converted to:
>
>int i = 64;
>
>at compile time by the compilers I use.  However:
>
>inlint int convert( int b ) { return b * 8; }
>
>Often is not.  And if functions become even a bit more complex than the
>above, they will usually become evaluated at run-time.

At least my compiler can optimize the inline-version of the convert-function,
if I tell it that b is not changed in the function, i.e:

  inline int convert (const int b) { return b * 8; }

The const tells compiler, that it doesn't have to use a temporary variable
when it passes the parameters, and a smart compiler can therefore calculate
the value of the function in compile time. Without the const-keyword it's
much less likely that compiler can/will optimize it.

IMHO one should use const parameters to functions when they are not
changed in the function, so that the compiler has better chances in optimizing
it.
---
  Esa Pulkkinen, esap@cs.tut.fi