On Tue, May 21, 2013 at 4:35 PM, Niall Douglas <ndougl...@gmail.com> wrote:
> standardised TM support into the standard. Call it "TM lite" in a nod to
> "Concepts Lite":
You can call it "Low-level TM".
... while what we want is a language support. Some optional parts can
be implemented in libraries.
BTW, "Lite" is a missing leading postfix to me after I found "Concept-lite"
is a broken "Concept" and essentially a better "static if".
It's far easier to change a standard library than a standard language feature.
One of the informally discussed topics at C++ Now was that we're going to have to break existing futures and promises, because C++11 made some mistakes not obvious at the time. The fix, while difficult because of the code that may break, is doable.
On Wed, May 22, 2013 at 11:56 AM, Niall Douglas <ndougl...@gmail.com> wrote:
> It's far easier to change a standard library than a standard language
> feature. And for good reason: libraries can be replaced without standards
> change.
I pretty much sure you don't want to write __cxa_throw instead of
a `throw` keyword, or write a __cxa_begin_catch... __cxa_end_catch
pair instead of a `catch` block. For the same reason, begin_trans...
commit_trans... make my eyes bleeding.
> Andrew's approach to Concepts is not without its problems, as Andrew will
> tell you himself. But we live in a practical world: Andrew asked what's
> possible now rather than what's desirable (later after much lengthy debate).
> And if even static if isn't possible due to lack of consensus, you can see
> what standards formers are facing. Meanwhile, any developers using
> metaprogrammed libraries today waste tens of thousands of man hours, which
> adds up to billions of dollars of lost productivity. Andrew's proposal could
> halve that cost. For that alone, it is worth it.
I suppose you know "concept-lite" only works on syntax level. It's nothing
but a set of macros which can do conjunction and disjunction. C++ has *no*
feature only works on the syntax level other than the __cplusplus macro.
> We work with the hand we're dealt. That includes TM as well: consensus must
> be found, and that often means a huge amount of compromise, indeed throwing
> away decades of exploratory work. Such is ISO standards.
If a library solution works, it's considerable; however, I want to
point out that your solution does not work -- transactions can
not be composed (one embedded into another), since the
unwind_ctx is explicit.
Yes it is easier, in fact considerably so. There is an additional track for library changes allowing it to evolve a lot quicker (the TR process). Boost lets you implement an alternative library and if it gains widespread acceptance then a subsequent TR can and does merge those Boost changes into the libc++. Typical times for library changes are about five years, as against more than ten years for language changes. It's also the case that changing compilers is vastly harder than changing libraries.
--
You received this message because you are subscribed to the Google Groups "SG5 - Transactional Memory" group.
To unsubscribe from this group and stop receiving emails from it, send an email to tm+unsu...@isocpp.org.
To post to this group, send email to t...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/tm/?hl=en.
On Wed, May 22, 2013 at 1:58 PM, Niall Douglas <ndougl...@gmail.com> wrote:
> No one is proposing that anyone use the API I posted directly. It would
> almost certainly be wrapped in C++ RAII utility classes etc.
If the things you showed are underlying APIs, then they are
already there:
http://gcc.gnu.org/wiki/TransactionalMemory?action=AttachFile&do=view&target=Intel-TM-ABI-1_1_20060506.pdf
I'd like to see the interface you want, or the users want, based
on your suspect.
There is no need to test, and SG is not a river in front of you. Just FYI:
C++ does not accept macro solutions, since the need of a macro always
implies a need of a language feature.
On 05/21/2013 10:35 PM, Niall Douglas wrote:
> after Michael's talk, I
> heard a number of people on the core committee informally express
> very similar worries to mine about the potential of the current
> proposal to break existing code.
Then, I'm really surprised to hear that the mere presence of TM
support in the compiler would be suspect of breaking existing code.
Given our (current) choice of double-underscore keywords, is
there anything a bit more detailed (such as an example) that you
could relate?
> Whether these are unfounded or not
> isn't the important part: /the important part is getting TM into
> C++/, not some particular implementation of TM into C++. And right
> now the current proposal /appears /too complex to give peace of mind
> to a lot of folk.
Noted. Some of this might be fear of the unknown, though.
> So, given this, here is my alternative minimal *initial *proposal for
> TM support in C++. I stress the "initial" part: We start with this
> and add stuff. I can guarantee everyone on this SG will hate it, but
> I think it's worth bringing up for discussion as an alternative route
> to getting standardised TM support into the standard. Call it "TM
> lite" in a nod to "Concepts Lite":
>
> // transaction context is opaque compiler-internal type
> typedef void *trans_ctx;
> // unwind context is opaque compiler-internal type
> typedef void *unwind_ctx;
>
> // Start a transaction. Can be called many times. Can return NULL if ENOMEM (or HTM has run out of free contexts).
> trans_ctx begin_trans();
>
> // Makes the transaction visible if possible, returning false if not possible. Deletes ctx if successful.
> _Bool commit_trans(trans_ctx ctx);
>
> // Deletes ctx
> void abort_trans(trans_ctx ctx);
>
> // Loads and stores
> template<typename T> T trans_load_explicit(trans_ctx ctx, volatile T* obj, memory_order order);
> template<typename T> void trans_store_explicit(trans_ctx ctx, volatile T* obj, T desired, memory_order order);
What's the "volatile" doing here? It shouldn't be there (also below).
> // These equal the explicit forms with order=memory_order_relaxed
> template<typename T> T trans_load(trans_ctx ctx, volatile T* obj);
> template<typename T> void trans_store(trans_ctx ctx, volatile T* obj, T desired);
So, how does the memory order given here interact with 1.10? These are not atomic
variables, so they don't plug into 1.10 as-is.
> // Exchanges ...
> template<typename T> T trans_exchange_explicit(trans_ctx ctx, volatile T* obj, T desr, memory_order order);
>
> // API continues duplicating the C11 atomic API but where non-explicit functions default to memory_order_relaxed
I'd presume we'd make trans_ctx a C++ class so that we can make these member
functions, right?
> // Create a stack unwind transaction for this execution context (i.e. curly braces). Can be called many times. Can return NULL if insufficient stack space.
> unwind_ctx begin_unwind_trans();
> // Registers an unwind transaction to occur when the specified execution context exits.
> void commit_unwind_trans(unwind_ctx ctx);
>
> // Unregisters a previously registered unwind transaction.
> void abort_unwind_trans(unwind_ctx ctx);
>
> // Add a filtering callback. Gets called when ANY unwind passes through ctx. Callback is passed all unwinds currently in progress.
> // ctx=NULL means filter is global.
> void unwind_trans_filter(unwind_ctx ctx, void (*filter)(size_t no, unwind_ctx *ctxs, size_t idx, void *data), void *data);
>
> // Loads and stores
> template<typename T> T unwind_trans_load_explicit(unwind_ctx ctx, volatile T* obj, memory_order order);
> template<typename T> void unwind_trans_store_explicit(unwind_ctx ctx, volatile T* obj, T desired, memory_order order);
>
> // These equal the explicit forms with order=memory_order_relaxed
> template<typename T> T unwind_trans_load(unwind_ctx ctx, volatile T* obj);
> template<typename T> void unwind_trans_store(unwind_ctx ctx, volatile T* obj, T desired);
>
> // Exchanges ...
> template<typename T> T unwind_trans_exchange_explicit(unwind_ctx ctx, volatile T* obj, T desr, memory_order order);
>
> // API continues duplicating the C11 atomic API but where non-explicit functions default to memory_order_relaxed
> ...
I'm sorry, I don't know what this unwinding stuff is supposed to do.
> In other words, the absolute minimum C API possible for coping with
> TM and stack unwinding TM. On HTM capable hardware, the compiler
> generates the appropriate HTM instructions. Without HTM capable
> hardware, support is emulated. The stack unwinding stuff basically
> opens the existing compiler's stack unwinding machinery to programmer
> control. The above enables TM availability to both C and C++ code,
> with HTM acceleration where available, in a completely
> non-threatening and non-intrusive way.
I'm sure there are software transactional memory implementations out there
that provide an explicit function call interface similar to the one you
proposed above (minus the unwinding part).
- This not easy to use. The compiler should be able to do the instrumentation.
- This interface allows arbitrary non-transactional memory accesses between
transactional ones. As far as I understand, that's not what HTM supports,
where ALL memory accesses after "tx begin" are transactional, until "tx commit"
is reached.
TLS is part of C11, as _Thread_local, so it's not an issue.
Unwinding does exist in C, so C must not say anything
about it. Maybe we want a new set of __cxa_* APIs for TM,
but that's not WG14 nor WG21's job... Same as alloca(),
which is a POSIX API -- it's not our job to define it.
Hi Niall,
Thanks for your feedback. I agree we need to think about what we really need to include in the proposal, and omit--at least for now--extraneous features. But it's also important to keep in mind what our goals are, so that we don't throw out features important to these goals.
To limit the scope of this discussion, I want to clarify that I think your proposal covers only the part of the proposal related to *atomic* transactions, not relaxed ones. (Semantically, in the absence of atomic transactions, relaxed transactions are equivalent to critical sections guarded by a single global lock.) Please correct me if I am misunderstanding you on this point.
Even for atomic transactions, here is not a single goal, but among the goals are to provide a simpler, more effective mechanism than locks and atomic variables for synchronization in concurrent C++ programs and to exploit hardware features (i.e., hardware TM) we expect in some future processors. Transactional constructs in C++ are, we believe, a *means* to such goals, not the goal itself.
> I think you misunderstood what I propose (my fault). If I do:
>
> int dest=1, src=4, foo=5;
> trans_ctx h=begin_trans();
> trans_store(h, &dest, foo+trans_load(h, &src));
> src=2;
> foo=10;
> assert(dest==1); // works
> if(trans_commit(h)) // make everything written to by trans_ctx visible atomically
> assert(dest==12); // works
>
> In other words you *cannot* interleave non-transactional and transactional accesses. trans_ctx is simply to tell the compiler to compile any code working on the trans_ctx into a separate block of code whose effects on memory are to be made visible atomically. In the above case, the direct load of foo in the trans_store() gets converted by the compiler into an implicit trans_load(h, &foo).
>
> In other words, think of trans_ctx operations as being like a lambda. begin_trans() starts the lambda. trans_*() functions write what the lambda will do. trans_commit() calls the lambda.
Thanks for this clarifying example. I did not know that this was the semantics you intended--indeed, I didn't know what semantics you intended. I'm curious though about why the direct load of foo should be converted to an implicit trans_load. If so, why put the trans_load explicitly for src? Why have two ways to say the same thing?
And when would such an implicit conversion take place? For example, what if there is a function call within the third argument expression to trans_store? What if that function does I/O?
> any one of which is enough to bring in significant hesitation. Also
> nothing is obvious about how TM enabled C++ interops with C other than
> calling C anything auto-aborts any TM.
I don't understand this sentence.
> They *are* atomic variables :). Just with the non-explicit variant
> defaulting to relaxed.
Atomic operations are not transaction_safe. They are supposed to be a
single atomic thing that doesn't give the guarantee of extending the
atomicity guarantee to anything else (ie, you can't just merge two
atomic accesses). Transactions do give those guarantees. It's not the
same thing.
> My proposal is not a library. It's a C API for telling the compiler
> how to compile code.
So it's directives for the compiler expresses through a library
interface. And because it's not supposed to be a library, it must
conceptually be a language-level feature, right? So, in the end, you're
trying to express a language-level feature by telling the compiler about
it with a library-level interface?
So the compiler has to do the same that it has to do with
__transaction_atomic, just that we're requiring users to use a much less
compact interface? And we change the (language-level) semantics of the
code between begin and end? I don't see any benefit to a clean
language-level construct. This makes it harder for a compiler to figure
out when a transaction ends, and which code has to be instrumented.
It's easier for programmers to make a mistake with this interface. And
we have the same or even more changes to semantics you were worrying
about.
> In other words, think of trans_ctx operations as being like a lambda.
> begin_trans() starts the lambda. trans_*() functions write what the
> lambda will do. trans_commit() calls the lambda.
But you said that the compiler converts foo=10 into a trans_store(). So
there's more than just setting up a static transaction. If it really
were like a lambda, why not use a lambda right away but design a very
similar feature?
> I agree that use of the C11 atomics API severely limits what you can
> do inside a transaction e.g. no branch logic. That is deliberate: the
> API is intended to force programmers to work in small, chainable,
> unwindable atomic chunks of memory effect. As I mentioned before, no
> one is proposing that programmers use this API directly.
So who would be the actual target audience of this API, if it isn't C++
programmers?
> And as I also mentioned before, I'm not aiming for feature parity, I
> want the most useful 80% of what TM can provide, which for me is
> mainly multi-CAS and multi-atomics.
If that's what you're interested it, why not just work on a clean API
for atomic operations on multi-memory locations (e.g., mCAS, atomic
snapshots, ...)? Or call them static transactions, if you like (static
in the sense of which accesses and which operational semantics (from a
fixed set of semantics such as CAS)). Those could perhaps even be
considered independently of TM, although they should likely be able to
synchronize with transactions as specified by SG5. Define the precise
semantics wrt. the memory model, and propose this API to C++ SG1/SG5.
IMO, and I guess for most here, TM is about transactions that can access
any memory locations, don't have to declare which they are going to
access up-front at the start of a transaction, and allow programmers to
express any kind of operational semantics using transactions (eg, do
branch). Of course we'll have some restrictions as only allowing
transaction_safe code in atomic transactions, but the goal is to not try
to come up with a general mechanism. Which is another reason why it
will probably be cleaner as a language construct than a library.
On May 24, 2013, at 11:11 AM, Niall Douglas wrote:
> [snip]
>
> I think what most C++ users will mostly want from TM is
> multi-atomics. The rest they can live without. I think you'll see that
> conservatism appear when you submit your TS. They'll ask "do we really
> need this?" to which the answer is "no". The next question will be
> "what from this do we actually need?" to which is the answer I think
> is "multi-atomics". You'll get that support and that support alone
> because the rest won't be absolutely necessary, and C++ TM v1.0 will
> be considered done.
If all we really wanted were "multi-atomics," I would be inclined to go
with something much simpler than what Niall is proposing -- namely, a
library-based MCAS that doesn't even try to look like TM
On 05/24/2013 05:11 PM, Niall Douglas wrote:
> It's more observations than feedback. Full fat TM is cool and
> everything, but it comes across as being similarly deep as full fat
> Concepts. We all have bad memories of how the committee and vendors
> reacted to full fat Concepts.
The way this is expressed does not match my recollection of the
WG21 standards meetings where concepts were discussed.
Note that concepts were directly integrated into the C++ standard.
Nowadays, we're on a more fine-grained schedule, and TM is heading for
a TS, not the C++ standard proper, in the first round. Both facts
make me believe we'll not hit roundabout opposition in WG21.
The static versions, along the lines Michael suggests don't require any
compiler support, but also make the programming model much harder (think
about ABA). Niall's proposal, to the extent I understand it, still
requires compiler support to translate code used in transactions, and as
others have pointed out, if we're doing that anyway, we might as well
make the syntax nice.
On Fri, 2013-05-24 at 08:42 -0700, Niall Douglas wrote:
You said that the compiler would be required to transform normal code
located between different calls to intrinsics. So you are using the
calls to demarcate a region of code, and then require the compiler to
not just replace the intrinsics calls with something else but actually
instrument all the code in between, etc. That's unlike the typical
intrinsics I'm aware of, and much more like a language feature. Just
uglier.
Do you think that vendors would prefer having to agree on one set of
intrinsics that isn't just calls but demarcates code, and thus brings in
lots of the issues a language-level construct would face, instead of
having a properly defined language-level construct right away?
IMO, if what you're proposing does need custom compiler support (ie,
it's not just a library), then you'll see the same opposition from
compiler vendors as you claim will come up for a language extension.
> Sure. You guys have been at this for fifteen years or so. I think what
> you've achieved is amazing, but it still comes across as being full of
> dragons to the C++ language standard. ISO standards are supposed to
> standardise existing practice, not new practice, and that's the
> benchmark by which they are judged.
How do you think would your proposal score according to this benchmark?
On Fri, 2013-05-24 at 08:11 -0700, Niall Douglas wrote:
> I think what most C++ users will mostly want from TM is multi-atomics.
> The rest they can live without. I think you'll see that conservatism
> appear when you submit your TS. They'll ask "do we really need this?"
> to which the answer is "no". The next question will be "what from this
> do we actually need?" to which is the answer I think is
> "multi-atomics".
If you haven't yet, please try building a simple concurrent operation,
say, a concurrent linked list with this kind of static transaction.
Don't cheat regarding ABA issues and such, as Mark pointed out. Are you
still convinced this is the static transactions are quite 80%?
So which operations do you actually want?