XACT: Lock-Free Multi-CAS for C++/x64 Built on TSX
github.com
github.com
See https://software.intel.com/en-us/articles/tsx-anti-patterns-... and https://software.intel.com/en-us/blogs/2013/06/23/tsx-fallba... for more details/
To make his code work he likely would need a global fallback lock (or a real STM) and guarantee that every change of the touched memory uses those too (which would be hard)
So I'm afraid the library is fairly broken.
The transaction mechanism doesn't know in advance if it's a good or a bad page fault.
You would need to tell the operating system kernel that the page fault happened in a transaction, and let it ignore it if it was a bad page fault. That would be much more complicated than current TSX.
Also there are other cases were retries will not succeed, page fault was just an example. Another common case is the dynamic linker when a library function is first executed.
It would allow bad page faults to crash the program, i.e., ordinary behavior. No? Why do programs need this protection for HTM transactions?
> You would need to tell the operating system kernel that the page fault happened in a transaction, and let it ignore it if it was a bad page fault.
It wouldn't ignore it. It would fault the thread and probably tear down the process, as usual. No?
> Also there are other cases were retries will not succeed, page fault was just an example. Another common case is the dynamic linker when a library function is first executed.
That would be an abort due to excessive memory use?
Thanks! I'm not as familiar with this stuff as I would like to be.
Looking back over the readme, I can see how the loops used in the examples are a little misleading.
This is mostly a documentation issue: the core XACT code doesn't use infinite retry loops, and actually does not retry transactions at all. As with std::atomic, the goal is to provide a basic primitive and leave retry / backoff / etc. up to the user. This is especially important with lock-based fallbacks, as I can't pick one perfect lock to fit everyone's workload.
I ended up dropping retries because I ran into so many never-ending transactions in my early experiments with TSX. That was also my motivation for limiting the transactions to as few locations as possible.
I'm just now starting to reexamine this and add some configurable retry logic back in -- e.g. the retry policy here is used in some test code: https://github.com/scivey/xact/blob/master/include/xact/atom...
As to the difficulty of protecting any memory touched in a transaction under a locking scheme: that kind of problem is exactly why XACT is focused on CAS-like operations on relatively limited sets of memory addresses.
Can you elaborate on the global lock? What's the motivation there?
(See Anti pattern #4 in the link above)
A global lock is usually the simplest fall back path, and the performance can be good enough because it's just a slow path. Of course it's always possible to do something more complex.
Re: adding a counter into the read set, I think the new generalized API here will support that out of the box: https://github.com/scivey/xact/blob/master/docs/api/generali...
Thoughts?
Your unit tests don't seem to do it right.
It would probably be easier to hide the lock in your library, and enforce all other access to follow the right protocol using some ADTs. But then you just have a simple hardware TM accelerated STM.
FWIW the sweet spots for nice to use TM APIs are currently either lock elision, or compiler assisted TM (like __transaction* in gcc), or higher level libraries.
http://www.anandtech.com/show/8376/intel-disables-tsx-instru...
So sure, there are some systems in the wild that are broken, but probably not that many.
The risk of subtle locking bugs in multi threaded applications due to CPU bugs makes me want to shy away from the entire feature.
How do you deal with group size limitations? My understanding is that the hardware transactional support makes no forward progress guarantees specifically because it's bound by what it can monitor in the cache. So if the group size is too large, then transactions can keep failing. Hopefully I am not missunderstsnding this. If this is correct it means libraries of this nature have to take a position with regards to group size limits.
So what is your approach?
TSX is a black box in many ways, and I think we can expect its behavior to change over time and across implementations.
I'm not enforcing an arbitrary limit on transaction size because the primary goal is just to expose a simple C++ API to fundamental primitives. The TSX intrinsics are much more difficult to work with, and assembly is painful.
If that seems like a cop-out, consider that DCAS is effectively a transaction size of two. TSX appears to handle this trivially. Yet DCAS is already a very powerful operation, and is useful in itself.
As the docs emphasize, the goal is not general transactions but extended versions of the small atomic operations already in common use.
In terms of safety and opinionatedness, I think of XACT like a library of locking primitives: pthread_spinlock_t is very useful, but it will not stop you from introducing deadlocks. Likewise, I won't stop you from attempting transactions that are too large to succceed on current hardware. Ultimately, I expect anyone using this to test and benchmark their own code on their own machines.
Beyond a certain size, transactions will be less and less valuable even if they can be successfully completed: if you're attempting 64-way CAS, benchmarks are probably going to guide you toward traditional locking anyway.
TSX is somewhat unpredictable as a general tool, and there are difficulties with e.g. knowing which transactions are even feasible. Generic "complicated transactional operations" also make lock-based fallbacks very difficult and expensive, which andikleen2 also touched on.
After experimenting with more general use of TSX, I very quickly came not to trust it. So the real motivation here is to tame TSX's unpredictability by using it in a very controlled way.
TSX simply isn't suitable yet for complicated transactions, but just providing hardware-level support for multi-CAS is already a big deal.