At this point, I must add that most of my experience with 'volatile' has
been in the typical case where a variable can change by something outside
the process it's running in.
My co-worker, however, believes that the usage of "volatile int m_x" in the
following example is not only necessary for portability but is good
documentation:
Let's say we have class foo defined as follows:
class Foo{
protected:
volatile int m_x;
friend void ThreadA(..);
friend void ThreadB(..);
};
Assume that MyFoo is a variable that both threads A and B have a pointer to.
Furthermore, assume that "MyFoo.m_x = 0" prior to either thread execution.
The following threads are started at the same time:
void ThreadA(..){
CFoo *pMyFoo=[get a pointer to MyFoo];
while(1){
if(*pMyFoo == 1) break;
}
[Do some work]
}
void ThreadB(..){
CFoo *pMyFoo=[get a pointer to MyFoo];
[Do some work]
*pMyFoo=1;
}
My coworker believes that an "aggressive" optimizer may put the check for
(*pMyFoo == 1) outside the while loop in ThreadA since "optimizers have
functional scope" and will not know that *pMyFoo may be changed somewhere
else. By using the volatile key word, you are guaranteed that something like
that will not happen.
I, on the other hand, believe that it would be wrong for an optimizer to do
such a thing and therefore the usage of the volatile keyword here is not
only "unnecessary" but also misleading.
I would appreciate any input.
-Ali
http://intel.forums.liveworld.com/thread.jsp?forum=242&thread=6286&message=19622#19622
regards,
alexander.
Code re-ordering is a common optimization for a modern compiler to use.
Compilers generally assume single-threaded execution. When multiple threads
have otherwise write access to a data element then the element's value can
change at random from the compiler's perspective. Volatile tells the
compiler that the affected data elements can change in ways that the
compiler cannot predict.
- Jeff.
"Ali Ghorashi" <ali.ghorashi...@lmco.com> wrote in message
news:bc7qdr$dn...@cui1.lmms.lmco.com...
[...]
> void ThreadA(..){
> CFoo *pMyFoo=[get a pointer to MyFoo];
>
> while(1){
> if(*pMyFoo == 1) break;
> }
> [Do some work]
> }
>
> void ThreadB(..){
> CFoo *pMyFoo=[get a pointer to MyFoo];
>
> [Do some work]
> *pMyFoo=1;
> }
>
> My coworker believes that an "aggressive" optimizer may put the check for
> (*pMyFoo == 1) outside the while loop in ThreadA since "optimizers have
> functional scope" and will not know that *pMyFoo may be changed somewhere
> else. By using the volatile key word, you are guaranteed that something
like
> that will not happen.
The section 1.9/7 of C++ standard says this about volatile:
"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.7)"
Which I believe means that the "aggressive" optimization your coworker is
talking about is not valid according to the C++ standard for volatile
variables.
That being said, ISO C++ is totally agnostic with respect to
multi-threading.
Whatever problem the volatile keyword is supposed to solve, does not resolve
any multi-threading issues. You will have to use platform specific calls for
this, or a library that wraps those calls in a nice package.
<ot>
Even though may get away with the code you posted on your system, platforms
facilities for thread synchronisation should be preferred instead. In your
example ThreadA is burning CPU cycles while waiting for ThreadB to complete.
On a single processor system that could very well mean that ThreadB takes
twice as long to complete because ThreadA is consuming 50% of the available
CPU cycles. With the thread synchronisation methods offered by the platform
ThreadA doesn't have to consume CPU cycles until ThreadB has signalled it
has finished its job.
</ot>
--
Peter van Merkerk
peter.van.merkerk(at)dse.nl
> Hello All,
> One of my coworkers and I had an argument over the usage of the 'volatile'
> keyword which I was hoping some of you may be able to shed some light on.
> First, the K&R book says the following about the volatile keyword:
> "The purpose of volatile is to force an implementation to suppress
> optimization that could otherwise occur." And then goes on to give a typical
> example of a variable being changed by hardware.
...
The following article is just what you need:
http://www.cuj.com/documents/s=7998/cujcexp1902alexandr/alexandr.htm
Andrei Alexandrescu about volatile and thread safety.
Assaf
In order to answer your question one first need to now what exactly is meant
under '*pMyFoo == 1' and '*pMyFoo = 1'. I don't see any user-defined
assignment and/or comparison operators in your code, which means that these
expressions are invalid. Is it a typo of some kind? Or you simply forgot to
include the definitions of the above operators?
--
Best regards,
Andrey Tarasevich
Brainbench C and C++ Programming MVP
Stop spreading that piece of mixture of "a few interesting thoughts"
and "big chunk of utter silliness", please.
http://groups.google.com/groups?selm=3D7F5630.178962B3%40web.de
http://groups.google.com/groups?selm=3D934ECB.8CD73030%40web.de
regards,
alexander.
P.S. Alexandrescu died yesterday night, BTW. An "accident". Some
critical soft was using C/C++ volatiles in place of proper sync...
and the effect of a race condition killed Andrei (details are still
kept secret but it is known that he died hard with prolonged and
extremely intensive pain, grieve, etc.). RIP (and "holy fuck").
I think that both you and your coworker are wrong.
volatile is useful for memory that may be modified by something
outside the process (e.g. hardware registers, etc.). Typically, the
volatile variable should be atomically updated by the hardware, or you
may get problems with word-tearing, etc.
For thread synchronization, you should use the multithreading
primatives to synchronize access. Volatile either won't work at all
for what you want (since it doesn't necessarily issue memory
barriers), or it will be a massive pessimization. Instead, you need
either atomic variables and atomic access primitives, or mutexes. In
the code you posted, you should also be using a condition variable and
notifying the waiting thread when it changes.
I'm not an expert, so I may have got something wrong - the best place
to ask about this is comp.programming.threads.
Tom
You're not entirely wrong. Originaly, C/C++ volatiles were indead
designed having memory mapped I/O ("registers"/"ports") in mind. At
that time hardware was rather simplistic and didn't do any reordering
in either "ordinal" or "I/O" space. Modern hardware COULD reorder
memory accesses in ALL spaces. So, you basically need almost the same
reordering constrains (see the links below) for portable device driver
programming as for threading. The situation is actually kinda more
complicated because for device drivers you'd need to "control" BOTH
spaces -- e.g. if you publish a bunch of structure addresses and those
structure meant to be read/write by some device driver you really need
to ensure "cross"-space ordering. Unfortunately, the upcoming <iohw.h>
and <hardware> still lacks facilities to do that... unless they really
want to impose "total ordering" if you use any IO read/write calls.
Later, volatiles were kinda {re-}used for setjmp/longjmp stuff in order
to spill the locals to memory (and to reload them after the jump).
Finally, volatiles are also needed for static sig_atomic_t's. Both
these uses of volatiles are purely "single-threaded". Ideally, we
should get rid completely of setjmp/longjmp (ISO C should adopt C++
exceptions.. but after they will be repaired, of course) and also
asynchronous signals (threads shall replace them). Given the upcoming
<iohw.h> and <hardware>, C/C++ volatiles could then be deprecated as
well. Now, back to threading and C++... here's some C++ code for
"curious" folks who can "speak C++":
http://www.terekhov.de/pthread_refcount_t/experimental/refcount.cpp
Some wording about msync "semantics" can be found here:
http://www.google.com/groups?selm=3EE0CA46.593F938B%40web.de
(Subject: Re: Asynchronous queue without synchronization primitives)
Reference counting operation are described here:
http://www.terekhov.de/pthread_refcount_t/draft-edits.txt
Comments are quite welcome. TIA.
regards,
alexander.
P.S. Tom, don't miss the use of enums for overloading. ;-)
>P.S. Tom, don't miss the use of enums for overloading. ;-)
Ahh, but that's in member functions, so that's ok! I only frown about
it in namespace scope functions, where it could potentially cause the
wrong overload to be called (e.g. an int overload instead of your enum
overload), or at least cause an ambiguity. ;o)
Tom
In my simple example, if I do not use "volatile", is it possible for a "bug
free" C++ compiler to perform loop optimization on the "while(1)" loop in
ThreadA? And by loop optimization I mean putting the statements with
constant values outside the loop.
Regards,
-Ali
"Ali Ghorashi" <ali.ghorashi...@lmco.com> wrote in message
news:bc7qdr$dn...@cui1.lmms.lmco.com...
Ali Ghorashi wrote:
>
> OK,
> I think some of you took my question the wrong way. I must mention that my
> example had a typo in it: "CFoo" and "Foo class" are the same thing.
> My question here is really not about thread synchronization but about the
> language keyword 'volatile'. My question is basically this:
>
> In my simple example, if I do not use "volatile", is it possible for a "bug
> free" C++ compiler to perform loop optimization on the "while(1)" loop in
> ThreadA? And by loop optimization I mean putting the statements with
> constant values outside the loop.
Sure. Thats one basic optimization which is done by compilers. If something
is constant and the compiler can proove that it is constant then there
is no point in calculating it over and over again.
In your example:
--
Karl Heinz Buchegger
kbuc...@gascad.at
Karl Heinz Buchegger wrote:
>
> Ali Ghorashi wrote:
> >
> > OK,
> > I think some of you took my question the wrong way. I must mention that my
> > example had a typo in it: "CFoo" and "Foo class" are the same thing.
> > My question here is really not about thread synchronization but about the
> > language keyword 'volatile'. My question is basically this:
> >
> > In my simple example, if I do not use "volatile", is it possible for a "bug
> > free" C++ compiler to perform loop optimization on the "while(1)" loop in
> > ThreadA? And by loop optimization I mean putting the statements with
> > constant values outside the loop.
>
> Sure. Thats one basic optimization which is done by compilers. If something
> is constant and the compiler can proove that it is constant then there
Should read:
If there is an expression inside a loop and the compiler can proove that it
is constant then there
> is no point in calculating it over and over again.
>
> In your example:
CFoo *pMyFoo=[get a pointer to MyFoo];
while(1){
if(*pMyFoo == 1) break;
}
the compiler deduces that there is no way that either the pointer
nor the object the pointer points to can change.
>OK,
>I think some of you took my question the wrong way. I must mention that my
>example had a typo in it: "CFoo" and "Foo class" are the same thing.
>My question here is really not about thread synchronization but about the
>language keyword 'volatile'. My question is basically this:
>
>In my simple example, if I do not use "volatile", is it possible for a "bug
>free" C++ compiler to perform loop optimization on the "while(1)" loop in
>ThreadA? And by loop optimization I mean putting the statements with
>constant values outside the loop.
The effect of C++ code is described in terms of:
a) reads from and writes to volatile variables.
b) output statements.
Providing a program produces the expected output and sequence of reads
and writes to volatile variables, it can do what it wants. e.g.
something like:
int main()
{
int i;
for (i = 0; i < 1000000; ++i)
{
}
std::cout << i << '\n';
}
can easily be optimized to:
int main()
{
std::cout << 100000 << '\n';
}
However,
int main()
{
int j;
volatile int& i = j;
for (i = 0; i < 1000000; ++i)
{
}
std::cout << i << '\n';
}
cannot have the loop optimized.
Tom
Thanks.
http://google.com/groups?selm=3A66E4A1.54EE37AD%40compaq.com
http://google.com/groups?selm=QJZo9.17%24X93.717939%40news.cpqcorp.net
regards,
alexander.