Lock elision in the GNU C library
lwn.net
lwn.net
TSX promises to lower the overhead dramatically in such cases. I don't think it's clear yet how it will be implemented, but Dave Kanter over at RWT has speculated it will leverage the cache coherence protocol: http://www.realworldtech.com/haswell-tm.
To support cache coherency, cores already communicate with each other about reads and writes to cache lines. Multiple cores can have a line in their cache, but if any core modifies their copy, they have to send a message to the other cores signaling that their old copies are invalid (somewhat more technical description: http://www.di.unipi.it/~vannesch/SPA%202010-11/Silvia.pdf).
Kanter speculates that TSX uses this existing mechanism to cheaply support transactions. During a transaction, each core keeps track of the cache lines it has read or written to during the transaction. If it gets a message that another thread has written to a line it read from, or otherwise read from a line it wrote to, it aborts the transaction. Otherwise, the transaction commits successfully. This theoretically allows transactions that don't conflict to be handled completely locally to the core.
Correct me if I'm wrong but this sounds like "shared-state multithreading will be faster, as long as you don't have any parallelism."
There is no free lunch in shared-memory architectures. But there are lots of techniques for addressing the problem. One of my favorite ones these days is to have a set of states, one per CPU, that each program writes to in its fast path, that can then be periodically merged into a global state.
Shared state isn't the problem--mutable shared state is the problem. That's what kills you from a cache point of view. But the mere possibility of mutations to shared state shouldn't needlessly make things more expensive when those mutations are rare or non-existent in practice. That's where TSX comes in.
Both of these are problems you can code around, but doing that would likely require greater effort and complexity.
It's unclear to me whether recovering from mis-speculation is more expensive than acquiring the lock up-front. In other words, is there overhead in the case that you almost always encounter actual contention?
It's also unclear to me how this can be done without instruction set support, as the paper claims. Without having the critical sections explicitly marked how can this be implemented?
Yes, those two cases are what I'd consider secondary benefits, that could be achieved straightforwardly (but not easily, as you note) without TSX. The primary case -- when the lock is fine-grained, and thread A does mutate a value that thread B does eventually read, but B's read doesn't come until A is already out of the critical section -- is the big win for well-designed software, IMO.
> It's unclear to me whether recovering from mis-speculation is more expensive than acquiring the lock up-front. In other words, is there overhead in the case that you almost always encounter actual contention?
On big x86 cores, there shouldn't be much performance overhead. x86 already has mechanisms for holding tons of in-flight memory operations in the core without releasing and/or committing them to the rest of the memory system. TSX simply specifies a new reason for holding them ("we're in the middle of an SLE transaction"). If your transaction is aborted because another core touched the lock, you can flush that buffer of memory ops and rollback to a checkpointed register file almost instantly. The paper describes a hardware predictor that decides whether or not to initiate a transaction on a given store; in any smart implementation, this predictor would incorporate information like "have I incurred expensive rollbacks due to transaction aborts on this store?" In that way, a smart implementation should tune itself to yield arbitrarily low average- or worst-case overhead over the non-TSX case.
> It's also unclear to me how this can be done without instruction set support, as the paper claims. Without having the critical sections explicitly marked how can this be implemented?
(No pedantry intended, I'm sure you already read this) It's explained in Section 3.3. They look for this pattern:
Initially, MEM[x] = A
STORE MEM[x] = B
...
...
STORE MEM[x] = A (original value)
They consider anything between these "silent store pairs" to be a potential critical section, and the predictor can choose to try to elide the two stores and form a transaction, if it wishes. It is always OK to elide these two stores, whether it's a "real" critical section or not, as long as nobody tries to read MEM[x] in the middle (in the memory model they assume, anyway).You definitely require ISA support if:
1) Your silent stores may have other side effects (I/O?)
2) You have synchronization primitives that don't fit the silent store pattern (ticket lock?)
3) You have a strong business interest in making the behavior of existing software, even at the microarchitectural level, as predictable as possible (Intel?)
And I agree with the grandparent: this is hugely exciting.
Very interesting article in any case.
IA instructions can optionally contain a prefix, such as LOCK, address and operand override (use 16, 32, or 64 bits), and repeat (REP). For example, in Linux, a single "REP STOS" (repeat store string) instruction is used to zero-out the kernel's BSS region. REP can also be added to instructions that don't use it, and the CPU will simply ignore it--this is defined behavior.
For HLE, using REP in front of certain instructions on a spinlock will begin or end a single memory transaction without actually writing the spinlock value. Of course, on older CPUs, the REP will be ignored and the spinlock read/write will occur. On CPUs that support HLE: if another memory agent accesses the spinlock, the transaction will be canceled.
There is talk of performance measurement, but no numbers in the article, probably because Haswell isn't released.
␄
† http://software.intel.com/en-us/blogs/2012/11/06/exploring-i...
That said, whether GHC can make more use of glibc's TSX support (as opposed to generating TSX instructions itself) than any arbitrary C program depends on any impedance mismatch that may exist between the elided lock wrappers and GHC's own STM semantics. No idea if GHC has a way to reason about libc-managed locks.