From: Joshua Maurice on
On Mar 21, 1:09 am, Andy Venikov <swojchelo...(a)gmail.com> wrote:
> Joshua Maurice wrote:
> >Leigh Johnston wrote:
> >> Obviously the volatile keyword may not cause a memory barrier instruction to
> >> be emitted but this is a side issue. The combination of a memory barrier
> >> and volatile makes multi-threaded code work.
>
> > No. Memory barriers when properly used (without the volatile keyword)
> > are sufficient.
>
> Sorry Joshua, but I think it's a wrong, or at least an incomplete,
> statement.
>
> It all depends on how memory barriers/fences are implemented. In the
> same way that C++ standard doesn't talk about threads it doesn't talk
> about memory fences. If a memfence call is implemented as a library
> call, then yes, you will in essence get a compiler-level fence directive
> as none of the compilers I know of are allowed to move the code across a
> call to a library. But oftentimes memfences are implemented as macros
> that expand to inline assembly. If you don't use volatile then nothing
> will tell the compiler that it can't optimize the code and move the
> read/write across the macroized memfence. It is especially true on
> platforms that don't actually need hardware memfences (like x86) since
> in those cases calls to macro memfences will expand to nothing at all
> and then you will have nothing in your code that tells anything about a
> code-migration barrier.
>
> So is volatile sufficient - absolutely not. Portable? - hardly.
> Is it necessary in certain cases - absolutely.

Perhaps I was a bit too strong in my statement. I did intent to say
"for portable uses".

However, for future reference, what compilers of what versions on what
platforms implement volatile with these semantics? I would like to
research these claims to be better prepared in this discussion and
future ones. Are any of these implementations not x86? These
implementations really don't provide a sane threading interface and
force the use of the volatile keyword for threading? Weird.

I still must ask, really? That would mean that all shared state must
be volatile qualified, including internal class members for shared
data. Wouldn't that big a huge performance hit when the compiler can't
optimize any of that? Could you even use prebuilt classes (which
usually don't have volatile overloads) in the shared data, like say
std::string, std::vector, std::map, etc.?

Can you wrap this horrible threading interface into sane functions
which provide the usual semantics instead of doing this horrible
volatile hackery? That's what I would strongly suggest if possible.

Finally, on x86s, not all memfences are no-ops, though perhaps we're
using different definitions of memfences. Referencing JSR-133 Cookbook
http://g.oswego.edu/dl/jmm/cookbook.html
The "StoreLoad" memory barrier is not a no-op on x86.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Mathias Gaunard on
On 21 mar, 08:09, Andy Venikov <swojchelo...(a)gmail.com> wrote:

> It all depends on how memory barriers/fences are implemented. In the
> same way that C++ standard doesn't talk about threads it doesn't talk
> about memory fences. If a memfence call is implemented as a library
> call, then yes, you will in essence get a compiler-level fence directive
> as none of the compilers I know of are allowed to move the code across a
> call to a library. But oftentimes memfences are implemented as macros
> that expand to inline assembly. If you don't use volatile then nothing
> will tell the compiler that it can't optimize the code and move the
> read/write across the macroized memfence.

Likewise, compilers do not move the code when it contains inline
assembly, especially if that assembly contains memory fences...

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Andy Venikov on
Joshua Maurice wrote:

>> So is volatile sufficient - absolutely not. Portable? - hardly.
>> Is it necessary in certain cases - absolutely.
>
> Perhaps I was a bit too strong in my statement. I did intent to say
> "for portable uses".
>
> However, for future reference, what compilers of what versions on what
> platforms implement volatile with these semantics? I would like to
> research these claims to be better prepared in this discussion and
> future ones. Are any of these implementations not x86? These
> implementations really don't provide a sane threading interface and
> force the use of the volatile keyword for threading? Weird.
>

I'm sorry if I wasn't clear in my previous post, but I was talking about
standard volatile behavior.

The standard places a requirement on conforming implementations that:


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

1.9.11
The least requirements on a conforming implementation are:
� At sequence points, volatile objects are stable in the sense that
previous evaluations are complete and
subsequent evaluations have not yet occurred.

That to me sounds like a complete enough requirement that compilers
don't perform optimizations that produce "surprising" results in so far
as observable behavior in an abstract (single-threaded) machine are
concerned. This requirement happens to be very useful for multi-threaded
programs that can augment volatile with hardware fences to produce
meaningful results.


> I still must ask, really? That would mean that all shared state must
> be volatile qualified, including internal class members for shared
> data. Wouldn't that big a huge performance hit when the compiler can't
> optimize any of that? Could you even use prebuilt classes (which
> usually don't have volatile overloads) in the shared data, like say
> std::string, std::vector, std::map, etc.?

Not at all!
Most multi-threading issues are solved with mutexes, semaphores,
conditional variables and such. All of these are library calls. That
means that using volatile in those cases is not necessary. It's only
when you get into more esotheric parallel computing problems where you'd
like to avoid a heavy-handed approach of mutexes that you enter the
realm of volatile. In normal multi-threading solved with regular means
there is really no reason to use volatile.


<snip>

Thanks,
Andy.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Leigh Johnston on


"Andy Venikov" <swojchelowek(a)gmail.com> wrote in message
news:ho5s8u$52u$1(a)news.eternal-september.org...
>> I still must ask, really? That would mean that all shared state must
>> be volatile qualified, including internal class members for shared
>> data. Wouldn't that big a huge performance hit when the compiler can't
>> optimize any of that? Could you even use prebuilt classes (which
>> usually don't have volatile overloads) in the shared data, like say
>> std::string, std::vector, std::map, etc.?
>
> Not at all!
> Most multi-threading issues are solved with mutexes, semaphores,
> conditional variables and such. All of these are library calls. That
> means that using volatile in those cases is not necessary. It's only
> when you get into more esotheric parallel computing problems where you'd
> like to avoid a heavy-handed approach of mutexes that you enter the
> realm of volatile. In normal multi-threading solved with regular means
> there is really no reason to use volatile.

Esoteric? I would have thought independent correctly aligned (and therefore
atomic) x86 variable reads (fundamental types) without the use of a mutex
are not uncommon making volatile not uncommon also on that platform (on
VC++) at least. I have exactly one volatile in my entire codebase and that
is such a variable. From MSDN (VC++) docs:

"The volatile keyword is a type qualifier used to declare that an object can
be modified in the program by something such as the operating system, the
hardware, or a concurrently executing thread."

That doesn't seem esoteric to me! :)

/Leigh


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Bo Persson on
Leigh Johnston wrote:
> "Andy Venikov" <swojchelowek(a)gmail.com> wrote in message
> news:ho5s8u$52u$1(a)news.eternal-september.org...
>>> I still must ask, really? That would mean that all shared state
>>> must be volatile qualified, including internal class members for
>>> shared data. Wouldn't that big a huge performance hit when the
>>> compiler can't optimize any of that? Could you even use prebuilt
>>> classes (which usually don't have volatile overloads) in the
>>> shared data, like say std::string, std::vector, std::map, etc.?
>>
>> Not at all!
>> Most multi-threading issues are solved with mutexes, semaphores,
>> conditional variables and such. All of these are library calls.
>> That means that using volatile in those cases is not necessary.
>> It's only when you get into more esotheric parallel computing
>> problems where you'd like to avoid a heavy-handed approach of
>> mutexes that you enter the realm of volatile. In normal
>> multi-threading solved with regular means there is really no
>> reason to use volatile.
>
> Esoteric? I would have thought independent correctly aligned (and
> therefore atomic) x86 variable reads (fundamental types) without
> the use of a mutex are not uncommon making volatile not uncommon
> also on that platform (on VC++) at least. I have exactly one
> volatile in my entire codebase and that is such a variable. From
> MSDN (VC++) docs:
> "The volatile keyword is a type qualifier used to declare that an
> object can be modified in the program by something such as the
> operating system, the hardware, or a concurrently executing thread."
>
> That doesn't seem esoteric to me! :)
>

The esoteric thing is that this is a compiler specific extension, not something guaranteed by the language. Currently there are no threads at all in C++.

Note that the largest part of the MSDN document is clearly marked "Microsoft Specific". It is in that part the release and aquire semantics are defined.


Bo Persson



--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]