what can be optimized?

G

Grizlyk

Hello.

What can be optimised in C++ code and how i can garantee stable
behaviour below

1.
Are expression "auto volatile" can deny removing as "unused temporary"
like this:

auto volatile const class_name tmp;

If not, how can i do it else?

2.
Can C++ garantee, that unnamed temporary object will not be removed as
"unused temporary" here:

class A{ A(){cout<<"start..."; } }
class B{ B(){cout<<"ok"<<endl; } }

void inp();
void test(){ A(); inp(); B(); }
 
P

peter koch

Hello.

What can be optimised in C++ code and how i can garantee stable
behaviour below
Anything might be optimised as long as visible behaviour is not
affected. Visible behaviour is anything output by the program, but not
e.g. how long time something takes.
1.
Are expression "auto volatile" can deny removing as "unused temporary"
like this:

auto volatile const class_name tmp; No.


If not, how can i do it else?

What is your problem? Optimisation should not cause problems (unless
the optimised code is wrong, of course).
2.
Can C++ garantee, that unnamed temporary object will not be removed as
"unused temporary" here:
Yes. Anything that has a side-effect will normally not be optimised
away. Also, removal of your temporary objects below would change output
which is visible behaviour.
class A{ A(){cout<<"start..."; } }
class B{ B(){cout<<"ok"<<endl; } }

void inp();
void test(){ A(); inp(); B(); }

/Peter
 
G

Grizlyk

peter koch said:
Anything might be optimised as long as visible behaviour is not
affected.

I said, that compiler and programmer can think about "visible
behaviour" differently. Compiler for example, often eliminate
"unusable" code if compiler think code is not used.
What is your problem?

In the case of temporary object compiler could remove object to the
point, where object will be used, for example:

{
int tmp=0;

if(!is_ready)return tmp;
return 1;
}

can be optimized to

{
if(!is_ready){ int tmp=0; return tmp; }
return 1;
}

Can "auto volatile" force object to be placed into stack, not into CPU
registers?

In the case of temporary object

class A;
class B;
void test(){ A(); inp(); B(); }

compiler could eliminate objects, that created but after that never
used:

void test(){ inp(); }

because A(); and B(); are never used unnamed objects.
 
R

Rolf Magnus

Grizlyk said:
I said, that compiler and programmer can think about "visible
behaviour" differently. Compiler for example, often eliminate
"unusable" code if compiler think code is not used.

The compiler doesn't just do random guesses about what isn't needed. It
usually removes code that actually isn't used, even if you might think it
is. There are some special circumstances in low-level programming (direct
interaction with hardware, operating system kernels and such) where more
control is needed. That's what volatile is there for.
In the case of temporary object compiler could remove object to the
point, where object will be used, for example:

{
int tmp=0;

if(!is_ready)return tmp;
return 1;
}

can be optimized to

{
if(!is_ready){ int tmp=0; return tmp; }
return 1;
}

It can even optimize it to:

{
if(!is_ready){ return 0; }
return 1;
}


Or it might even do:

{
return is_ready != 0;
}

But why would that be a problem?
Can "auto volatile" force object to be placed into stack, not into CPU
registers?

On some compilers, it does, but I'm not sure if that's required by the
standard.
In the case of temporary object

class A;
class B;
void test(){ A(); inp(); B(); }

compiler could eliminate objects, that created but after that never
used:

void test(){ inp(); }

because A(); and B(); are never used unnamed objects.

The compiler might optimize those objects away only if their construction
and destruction are guaranteed to have no side effects, i.e. if you
wouldn't notice it anyway.
 
G

Grizlyk

Rolf said:
But why would that be a problem?

Instead of "int tmp" can be "myclass tmp", so non-trivial ctor exist,
and I want to be shure, that myclass::myclass() ctor will be
unconditional executed. And I do not want rewite class's ctors into
static function calls.
The compiler might optimize those objects away only if their construction
and destruction are guaranteed to have no side effects, i.e. if you
wouldn't notice it anyway.

What means "to have no side effects"? The fact that A() and B() have
non-trivial ctors is side effect or not? Or it is incorrect to call
obects ctors or I must explicit write A::A() or B::B(). What standard
require?
 
?

=?iso-8859-1?q?Erik_Wikstr=F6m?=

What means "to have no side effects"? The fact that A() and B() have
non-trivial ctors is side effect or not? Or it is incorrect to call
obects ctors or I must explicit write A::A() or B::B(). What standard
require?

A side effect is when something that happens besides the fact that (in
this instance) an object is created. Consider the following:

class Foo {
Foo(int& i) { ++i; }
}

If we create a temporary with this object and pass an integer as a
parameter the ctor will increment the supplied integer, this is a
side-effect. This means that the compiler can not optimize away the
creation of the object since it would also affect the value of the
integer passed (unless the integer can also be optimized away.

If you instead have the following class:

class Bar {
Bar(int i) { ++i; } // Notice no &
}

The compiler can optimize away the construction of the object since
nothing but the objects existence depends on it.
 
K

Kai-Uwe Bux

Grizlyk said:
Instead of "int tmp" can be "myclass tmp", so non-trivial ctor exist,
and I want to be shure, that myclass::myclass() ctor will be
unconditional executed. And I do not want rewite class's ctors into
static function calls.

Don't worry: in cases like this, the constructors will be called unless they
do not add to the observable behavior. In other words, constructor calls
can be optimized away only if the static function calls that you would use
in their place could be optimized away, too.
What means "to have no side effects"? The fact that A() and B() have
non-trivial ctors is side effect or not? Or it is incorrect to call
obects ctors or I must explicit write A::A() or B::B().

No, you don't. You can call the constructor just fine by writing A(). More
precisely, upon evaluation of this expression, a temporary is created and
constructed by calling the default constructor.

Now, as far as optimization is concerned, the following might happen:

struct A {

char arr [200];

A () {
std::cout << "hello world!\n";
}

};

// ...

int main () {
A(); //(*)
}

The standard requires that line (*) prints "hello world!". However, the
compiler is allowed to observe that there is no need to allocate 200 bytes
on the stack. Thus, machine instructions to that extend maybe eliminated.
I.e., in fact the compiler may actually not create an object (region of
memory) but just inline the body of the constructor. The reason is that the
observable behavior does not differ either way.


Best

Kai-Uwe Bux
 
G

Grizlyk

Kai-Uwe Bux said:
No, you don't. You can call the constructor just fine by writing A(). More
precisely, upon evaluation of this expression, a temporary is created and
constructed by calling the default constructor.

Well, and is it a good (known) C++ idiom to do the temporary object, to
use ctor of object as ordinary function call or not good?

The goal to use unnnamed ctor calling:

1. old code use X() as object, and I do not want to rewrite ctors into
static functions or class into namespace. It seems to me, that easyer
to change ctors implementations.

2. class members of the object, used by ctor can be created
unconcditionally

class X
{
public:
A a; //a created independed from if( !is_ok() ) or not?
X(){ if( !is_ok() )throw a; }
};

If compare 2 with next example

void test()
{
A a;
if( !is_ok() )throw a;
}

If A has ctor, must "A::A()" be called befor "is_ok()" in both cases or
in the case of class member only? Does concrete sequence exist between
functions calls?
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

2. class members of the object, used by ctor can be created
unconcditionally

class X
{
public:
A a; //a created independed from if( !is_ok() ) or not?
X(){ if( !is_ok() )throw a; }
};

Yes, A's ctor will be called before X's ctor. These kinds of things are
easy to examine by creating small classes that prints some text in the
ctor (and perhaps dtor).
If compare 2 with next example

void test()
{
A a;
if( !is_ok() )throw a;
}

If A has ctor, must "A::A()" be called befor "is_ok()" in both cases or
in the case of class member only?

I assume you meant function-call only, since it's obvious that A's ctor
have to be called in test(). But to answer your question, yes A's ctor
will be called in both cases.
Does concrete sequence exist between functions calls?

Do you mean if there is a defined sequence in which the ctors of
member-objects will be called? If you have a class like this:

class X {
A a;
B b;
C c;
// ...
}

Then the ctors of the members will be declared in the order they were
declared, that's why you have to take some care when using initialization.

X::X(int a_, int b_)
: b(b_), a(b + a_) // OOPS!
{ }


In the above constructor to X the initialization of the members (ctor-
calls) will be in the order they were declared, so the initialization of
'a' witch 'b + a_' is no good since 'b' has not been initialized yet.
 
G

Grizlyk

Erik said:
Do you mean if there is a defined sequence in which the ctors of
member-objects will be called?

No. In the case ctor A() used (logically) as ordinary function call.
Does concrete sequence (fixed order) exist between ordinary functions
calls?

Can the example below

a();
if( !is_ok() )b();

be switched by compiler to

if( !is_ok() ){ a(); b();}

I think that compiler can not switch "a()" and "is_ok()" calls, and
probably if a() is ctor (a()==A::A()) can not also.
 
K

Kai-Uwe Bux

Grizlyk said:
No. In the case ctor A() used (logically) as ordinary function call.
Does concrete sequence (fixed order) exist between ordinary functions
calls?

Can the example below

a();
if( !is_ok() )b();

be switched by compiler to

if( !is_ok() ){ a(); b();}

I think that compiler can not switch "a()" and "is_ok()" calls, and
probably if a() is ctor (a()==A::A()) can not also.

You need to forget about the fact that A() involves a constructor call. You
have a statement sequence:

a();
if( !is_ok() )b();

The first statement is evaluated first. At the ";" all side-effects of its
evaluation a guaranteed to have taken place since there is a sequence
point. Then the if-statement is executed. This is part of the meaning of
the program. Any subsequence reordering of machine code instructions that
the compiler may perform for optimization must preserve the meaning of the
program (i.e., its observable behavior).

I wonder what made you think it could possibly be otherwise. It's not like
the basic principles of C++ all of a sudden cease to apply just because an
expression involves creating a temporary object.


Best

Kai-Uwe Bux
 
G

Grizlyk

Kai-Uwe Bux said:
You need to forget about the fact that A() involves a constructor call. You
have a statement sequence:

a();
if( !is_ok() )b();

The first statement is evaluated first. At the ";" all side-effects of its
evaluation a guaranteed to have taken place since there is a sequence
point. Then the if-statement is executed. This is part of the meaning of
the program. Any subsequence reordering of machine code instructions that
the compiler may perform for optimization must preserve the meaning of the
program (i.e., its observable behavior).
"there is a sequence point."
The definition "sequence point" is not known to me (i do not see any
C++ book speaking about "sequence point"), and i do not use "sequence
point" befor.
I wonder what made you think it could possibly be otherwise. It's not like
the basic principles of C++ all of a sudden cease to apply just because an
expression involves creating a temporary object.
"what made you think it could possibly be otherwise"
I think like this:
1.
in the following code

{
int i=0;
if( !is_ok() ){b(i);}
return;
}

The variable "int i=0" defined befor "b(i)". The usage of "i" is well
defined so compiler can move the definition "int i=0" into place of
usage in order to increase speed&size for false condition

{
if( !is_ok() ){int i=0; b(i);}
return;
}

(at least if i write compiler i could do it)

2.
the following code

{
int(0);
if( !is_ok() ){b();}
return;
}

The usage of "int(0);" is well defined also, and compiler will not move
"int(0);" into other place of code, but it can eliminate the expression
as "never used".

3.
If I will use "A();" instead of "int(0);" (assuming A() has at least
user defined ctor) I am sure that A() can not be moved to other place
of code, but I want to be shure, that "A();" will never be eliminated
as "int(0);", as "never used".

outcome
I just do not use ctor as ordinary function call befor, so do not
shure, that optimization rules will neber be applyed to my expression
"A();" as for "unused variable".
 
G

Greg

2. the following code

{
int(0);
if( !is_ok() ){b();}
return;

}

The usage of "int(0);" is well defined also, and compiler will not move
"int(0);" into other place of code, but it can eliminate the expression
as "never used".

3. If I will use "A();" instead of "int(0);" (assuming A() has at least
user defined ctor) I am sure that A() can not be moved to other place
of code, but I want to be shure, that "A();" will never be eliminated
as "int(0);", as "never used".

The compiler is free to rearrange, rewrite or eliminate any of the
above lines of code - however it sees fit. The only restriction is that
the "observable behavior" of the program must remain the same as it
would have been had the compiler produced a program that ran exactly as
it was written.

Now, the "observable behavior" of a C++ program consists of 1) the
program's reads and writes to volatile objects and 2) the program's
calls to library I/O routines. That's it. So unless the lines of code
above have some bearing on either program's I/O or its accessing of
volatile objects (and there seems to be little sign of either in the
above examples) then the compiler is likely to eliminate all of the
code above. So to answer your question - "what can be optimized?" So
far, the answer is - just about everthing.

After all, if no one can tell whether this code executes or not when
the program is run, then why would anyone care one way or the other?

Greg
 
K

Kai-Uwe Bux

Grizlyk said:
The definition "sequence point" is not known to me (i do not see any
C++ book speaking about "sequence point"), and i do not use "sequence
point" befor.

The C++ standard defines the terms observable behavior and sequence point as
follows:

[1.9/6] The observable behavior of the abstract machine is its sequence of
reads and writes to volatile data and calls to library I/O functions

[1.9/7] Accessing an object designated by a volatile lvalue (3.10),
modifying an object, calling a library I/O function, or calling a function
that does any of those operations are all side effects, which are changes
in the state of the execution environment. Evaluation of an expression
might produce side effects. At certain specified points in the execution
sequence called sequence points, all side effects of previous evaluations
shall be complete and no side effects of subsequent evaluations shall have
taken place.

Also, it says:

[1.9/16] There is a sequence point at the completion of evaluation of each
full-expression.

And of course there is a definition of a full-expression. Suffice it to say
that in

a();

there is a sequence point at the ";". That is, all side-effects from
evaluating a() have taken place and the computation has not progressed into
things that come after the ";".

I don't know why your C++ books do not mention this. The standard specifies
the order of execution by saying where sequence points are. Of course, book
authors are free to explain the rules any way they like (as long as what
they say is effectively equivalent to the standard).

I think like this:
1.
in the following code

{
int i=0;
if( !is_ok() ){b(i);}
return;
}

The variable "int i=0" defined befor "b(i)". The usage of "i" is well
defined so compiler can move the definition "int i=0" into place of
usage in order to increase speed&size for false condition

{
if( !is_ok() ){int i=0; b(i);}
return;
}

(at least if i write compiler i could do it)

2.
the following code

{
int(0);
if( !is_ok() ){b();}
return;
}

The usage of "int(0);" is well defined also, and compiler will not move
"int(0);" into other place of code, but it can eliminate the expression
as "never used".

But just because it has no side-effects with observable behavior.
3.
If I will use "A();" instead of "int(0);" (assuming A() has at least
user defined ctor) I am sure that A() can not be moved to other place
of code, but I want to be shure, that "A();" will never be eliminated
as "int(0);", as "never used".

outcome
I just do not use ctor as ordinary function call befor, so do not
shure, that optimization rules will neber be applyed to my expression
"A();" as for "unused variable".

I see. Anyway, A() is an expression, evaluation of which can not just be
optimized away if the statement has observable effects.

There is only one case where the compiler is allowed to optimize away
constructor calls with side-effects: under certain circumstances,
copy-constructor calls may be elided [see 12.8/15 for details]. Thus, you
should never write copy-constructors that do more than just copy
constructing objects. You cannot rely on them being called. This, however,
is unrelated to the cases you run into: you did not use copy-constructors.


Best

Kai-Uwe Bux
 
?

=?iso-8859-1?q?Erik_Wikstr=F6m?=

The definition "sequence point" is not known to me (i do not see any
C++ book speaking about "sequence point"), and i do not use "sequence
point" befor.
From the standard: "At certain specific points in the execution
sequence called sequence points, all side effects of the previous
evaluations shall be complete and no side effects of subsequent
evaluations shall have taken place."

If I will use "A();" instead of "int(0);" (assuming A() has at least
user defined ctor) I am sure that A() can not be moved to other place
of code, but I want to be shure, that "A();" will never be eliminated
as "int(0);", as "never used".

I don't think that there are any limitations on what the compiler can
do, just that the executable generated must for all intents and
purposes behave as if it was your code executing. Meaning that it might
very well remove your A(), but if it does it have to add something else
to give the same behaviour as if the A() had been there.
 
D

David O

The "sequence point" description helps in determining what is well
defined in C (and C++).

For example:

int f()
{
int i = 0;
int j = 0;
return g( ++i, ++j ); // Assume g() is pure function of two ints.
}

is well defined, as i and j are both incremented between the sequence
point at the semicolon after the initialization of j and the sequence
point at the entry to g(), but the order is unspecified (and
unknowable).

Ironically, if i and j were declared as volatile, the code becomes
undefined because the order of the writes to i and j become part of the
(potentially) observable behavior of the program.

The compiler is allowed to perform any rewriting of the code that has
the same observable behavior as the written code. Without any volatile
variables, the observable behavior of, say,

std::cout << f() << std::endl;

can be rewritten by the compiler as:

std::cout << g(1, 1) << std::endl;

even if f() is not defined to be inline. The compiler can keep
rewriting as long as desired, so if g was the extremely recursive
Ackerman function (http://en.wikipedia.org/wiki/Ackermann_function),
the code could be rewritten as:

std::cout << 3 << std::endl;

The compiler can also optimise the resulting machine operations provide
the observable behavior is unchanged. In other words a well-defined
program must act "as if" the code was executed exactly as written.

In the original question, Grislyk asked if volatile was needed to
prevent "unused" objects being removed. The rules of C++ do say that
volatile is needed to ensure that they are actually constructed and
destructed. In practice, this is not an issue, as C++ cannot optimize
away observable behaviour.

In the code:

class A{ A(){cout<<"start..."; } }
class B{ B(){cout<<"ok"<<endl; } }

void inp();
void test()
{
A();
inp();
B();
}

test() may be implimented directly something like this (don't take too
literally):

void test()
{
{
char tmp[sizeof(A)]; // Allocate memory.
new( tmp ) A; // Initialize memory by calling A::A(),
writes "start..."
(A *)(tmp)->~A(); // Destruct A (no-op)
}
inp();
B(); // Same as for A above.
}

A smart compiler will follow the "as if" rule and produce code
equivalent to this:

void test()
{
cout<<"start..."; // Only observable behavior in A::A()
inp();
cout<<"ok"<<endl;
}

In nearly all real situations, this is exactly what was intended in the
first place, actually allocating and constructing A and B being
incidental to the requirements.

Best Regards,

David O.
 
G

Grizlyk

Grizlyk said:
auto volatile const class_name tmp;

The "volatile" keyword can not be used, because
class_name::class_name() must be declared as "volatile" function.

I have decided (in order to do not fear possible optimization) to
rewrite class's ctors into set of global functions placed in the
namespace with the same name.

Already have done it. While all looks ok.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top