State Teleportation How Hardware Transactional Memory can Improve Legacy Data Structures Maurice Herlihy and Eli Wald Brown University
Transactional Memory
Concurrent Data Structure = State Machine atomic step memory barrier
Concurrent Data Structure = State Machine atomic step memory barrier critical section, CAS, load/store
“as if” sequence of transitions State Teleportation atomic step atomic step atomic step memory barrier memory barrier memory barrier Hardware Transaction “as if” sequence of transitions
Three Concurrent Lists lazy wait-free traversal + locks lock-free lock-free everything locking hand-over-hand locking
Hand-Over-Hand Locking b c d remove(b)
Hand-Over-Hand Locking b c d remove(b)
Hand-Over-Hand Locking b c d remove(b) Found it!
Hand-Over-Hand Locking b c d remove(b)
Lazy List a b c remove(b)
Lazy List a b c remove(b)
Lazy List a b c remove(b)
Lazy List a b c remove(b)
Lazy List a b c validate … remove(b)
Lazy List a b c Logical delete remove(b)
Lazy List a b c Physical delete
Lock-Free List CAS a b c d remove(c)
seconds to finish 100,000 operations 80% writes, 20% modifications Benchmarks median of 6 runs higher = worse seconds to finish 100,000 operations 80% writes, 20% modifications
Benchmarks every thread has a core 1-4
Benchmarks every thread has a hyperthread 5-8
Benchmarks 9-up
No Memory Management lazy lock-free locking
Memory Management? Locking: Lazy: Lock-Free: immediate re-use hazard pointers
Hazard Pointers Node* hazardRead(Node** object) { while (true) { Node* read = *object; hazard[myIndex] = read; membar(); Node* reread = *object; if (read == reread) return read; }
Hazard Pointers read pointer to object Node* hazardRead(Node** object) { while (true) { Node* read = *object; hazard[myIndex] = read; membar(); Node* reread = *object; if (read == reread) return read; } read pointer to object
Hazard Pointers announce hazard Node* hazardRead(Node** object) { while (true) { Node* read = *object; hazard[myIndex] = read; membar(); Node* reread = *object; if (read == reread) return read; } announce hazard
Hazard Pointers make announcement visible (expensive) Node* hazardRead(Node** object) { while (true) { Node* read = *object; hazard[myIndex] = read; membar(); Node* reread = *object; if (read == reread) return read; } make announcement visible (expensive)
Hazard Pointers reread and validate Node* hazardRead(Node** object) { while (true) { Node* read = *object; hazard[myIndex] = read; membar(); Node* reread = *object; if (read == reread) return read; } reread and validate
Without Memory Management lazy lock-free locking
With Memory Management lazy lock-free locking
Lock Teleportation a b c d
Lock Teleportation read transaction a b c d
Lock Teleportation read transaction a b c d
Lock Teleportation no locks acquired a b c d
Hazard PointerTeleportation read transaction a b c d
Hazard PointerTeleportation b c d
Hazard Pointer Teleportation read transaction a b c d
Hazard Pointer Teleportation no memory barriers a b c d
Without Teleportation lazy lock-free locking
With Teleportation lazy lock-free locking
Speedup lazy lock-free locking
Adaptive Jumps If transaction commits … Add 1 to next distance If transaction aborts… Cut next distance in half
Average Teleport Distance lazy lock-free locking
Average Commit Rate lazy lock-free locking
Abort Reasons 4 threads 8 threads capacity explicit conflict unknown
Conclusions True cost of concurrent data structures should include memory management Cost of hazard pointers can equalize lock-based and lock-free structures Adaptive Teleportation can substantially improve memory management costs for both lock-based and lock-free structures