OrderAccess Refactoring
Erik Österlund
erik.osterlund at lnu.se
Mon Jan 12 14:18:51 UTC 2015
Hi David,
Thank you for the feedback! :)
On 10/01/15 11:11, David Holmes wrote:
> acquire/release semantics are problematic because there is no single
> common accepted meaning. Even talking about acquire() and release() as
> independent operations is somewhat meaningless - it is only when they
> are paired, and fixed in place that they start to give meaning to things.
I agree it is a bit vague. The description in the comments about
preventing all memory accesses from floating up and down is the one I
recognize and it is conceptually good. But then it is later defined in a
table as acquire = Load(Store|Load) and release = (Store|Load)Store. The
reason is understandable - even though the description of
acquire/release is quite general, they are only intended and designed to
be used in certain contexts like release store and load acquire (with
variants). Outside such similar contexts, it would not give the
guarantees described.
This gets more confusing when you go on reading that fence is
conceptually acquire and release. It is true on the conceptual level
using the conceptual descriptions of acquire/release, but the actual
guarantees of release + acquire is StoreStore, LoadLoad and LoadStore
according to the tables below, while fence() also provides StoreLoad
constraints. I don't know about you but I find that a bit misleading.
I mean, it might be quite obvious for anyone using it to only use
acquire/release in the intended contexts where it makes sense and then
there is no trouble, but I guess it doesn't hurt to add some comments
making it explicit to avoid any confusion. We don't want confusion I think.
> load_acquire/store_release don't necessarily map cleanly to
> acquire()/release() as currently described in orderAccess.hpp; nor do
> they necessarily map cleanly to the same named operations on eg ARMv8;
> nor to similar named entities in C++11. As Paul stated these were
> introduced to map to the ia64 architecture - which I believe had
> somewhat different semantics.
They do map cleanly after I change acquire() and release() to compiler
barriers on TSO platforms (and fencing on PPC). Except some exceptions
like load acquire on PPC that was specialized for speed using twi-isync
and x86 on windows that don't need the stronger barriers since volatile
is more accurate anyway.
> This whole area is a minefield because there are different concepts of
> what "acquire" and "release" may mean - so you need to start with
> detailed definitions. Personally, and I know I am not alone, I abhor the
> acquire/release terminology and usage, I much prefer the unambiguous
> storeload, storestore etc. :)
Yes I think we need a better concrete guarantee/contract. The current
conceptual description of acquire/release is nice I think and useful
also for understanding when to use it, but I want to make it explicit
that release is only used in conjunction with writes and acquire only in
conjunction with loads (and swapping instructions etc), and therefore do
not give the same guarantees as described as the more general conceptual
description promises.
The concrete guarantee users can rely on is acquire = Load(Store|Load)
and release = (Store|Load)Store, and that's consistent with the
implementation and reasonable intended usage. Hence, it's fine to use
them in the way acquire/release is described now, iff these barriers
suffice to guarantee it, otherwise acquire/release should not be used in
the given context. Does that make sense?
> The current code often expresses things in a semantically incorrect way
> eg that:
>
> store_release(addr, val) == store(addr, val); release();
>
> but in practice the end code provides what is required (often providing
> a stronger barrier because that's all that is available on a platform).
I'm not sure I get what you mean by this being semantically incorrect. I
get it's suboptimal and might be slower triggering compiler barriers
that won't be necessary when joined, but semantically surely it should
be the same, right?
> And I agree with Karen - tackle correctness first then refactoring. IT
> will be too hard to track correctness otherwise. Personally, given I
> work in this code quite a bit, I like to see the complete set of
> definitions for a platform in one place. The duplication is something I
> can live with when it is mostly one-liners.
Okay no problem. I built the general variant first (because it was
easier since I don't have access to other platforms except mine haha).
But I fully understand yours and Karen's motivation so I will get back
to you when I manage to do it the code duplicated way so it's easier to
follow the evolution.
Oh and speaking of which, I find it very strange that store_fence and
release_store_fence is duplicated on x86 because we don't want to cast
away the volatile. This whole non-volatile thing seems a bit off to me -
surely all calls to OrderedAccess should be expected to be volatile,
nothing else would make any sense? And if so, why is store_fence the one
exception that is non-volatile while none of the others are? Unless
anyone knows there is a good reason for this, I'd like to make it
consistent with the rest of the code and make it volatile as well (and
hence get rid of some code duplication while at it). Permission granted? ;)
Thank you David for a nice discussion about this! :)
/Erik
More information about the hotspot-dev
mailing list