560 likes | 570 Views
Transactional Memory. Lecturer: Danny Hendler. The Future of Computing. Speeding up uni-processors is harder and harder Intel, Sun (RIP), AMD, IBM now focusing on “multi-core” architectures Already, most computers are multiprocessors.
E N D
Transactional Memory Lecturer: Danny Hendler
The Future of Computing • Speeding up uni-processors is harder and harder • Intel, Sun (RIP), AMD, IBM now focusing on “multi-core” architectures • Already, most computers are multiprocessors How can we write correct and efficient algorithms for multiprocessors?
A fundamental problem of thread-level parallelism Thread A Thread B . . Account[i] = Account[i]-X; Account[j] = Account[j]+X; . . . . . Account[i] = Account[i]-X; Account[j] = Account[j]+X; . . . But what if execution is concurrent? Must avoid race conditions
What is a transaction? • A transaction is a sequence of memory reads and writes, executed by a single thread, that either commits or aborts • If a transaction commits, all the reads and writes appear to have executed atomically • If a transaction aborts, none of its stores take effect • Transaction operations aren't visible until they commit (if they do)
Transactions properties: A transaction satisfies the following key property: • Atomicity: Each transaction either commits (its changes seem to take effect atomically) or aborts (its changes have no effect).
Transactional Memory Goals • A new multiprocessor architecture • The goal: Implementing nonblockingsynchronization that is • efficient • easy to use compared with conventional techniques based on mutual exclusion • Implemented by hardware support (such as straightforward extensions to multiprocessor cache-coherence protocols) and / or by software mechanisms
A Usage Example Account[i] = Account[i]-X; Account[j] = Account[j]+X; Locks: Lock(L[i]); Lock(L[j]); Account[i] = Account[i] – X; Account[j] = Account[j] + X; Unlock(L[j]); Unlock(L[i]); Transactional Memory: atomic { Account[i] = Account[i] – X; Account[j] = Account[j] + X; };
Transaction A Transaction C Transaction B ld 0xdddd ... st 0xbeef ld 0xbeef ld 0xdddd ... ld 0xbbbb Commit Violation! Commit ld 0xbeef Re-execute with new data Transactions interaction • Transactions execute in commit order Time 0xbeef 0xbeef Taken from a presentation by Royi Maimon & Merav Havuv, prepared for a seminar given by Prof. Yehuda Afek.
Software Transactional Memory for Dynamic-Sized Data Structures(DSTM – Dynamic STM) Maurice Herlihy, Victor Luchangco, Mark Moir, William N. Scherer III PODC 2003 Prepared by Adi Suissa
Motivation • Transactional Memory – simplifies parallel programming • STM – Software based TM • Usually simpler than Hardware based TM • Can handle situations where HTM fails • However: • It is immature (supports static data sets and static transactions) • It is complicated
Overview • Short recap and what’s new? • How to use DSTM? • Example • Diving into DSTM • Example 2 • Improving performance • Obstruction freedom
Transactions • Transaction – a sequence of steps executed by a single thread • Transactions are atomic: each transaction either commits (it takes effect) or aborts (its effects are discarded) • Transactions are linearizable: they appear to take effect in a one-at-a-time order
The computation model Starting transaction Read-Transactional(o1) Write-Transactional(o2) Read(o3) Write(o4) Commit-Transaction
The computation model • Committing a transaction can have two outcomes: • Success: the transaction’s operations take effect • Failure: the operations are discarded • Implemented in Java and in C++
Previous STM designs • Only static memory – need to declare the memory that can be transactioned statically • We want the ability to create transactional objects dynamically • Only static transactions – transactions need to declare which addresses they are going to access before the transaction begins • We want to let transactions determine which object to access based on information of objects read inside a transaction and this is why it is called Dynamic Software Transactional Memory
Overview • Short recap and what’s new? • How to use DSTM? • Example • Diving into DSTM • Example 2 • Improving performance • Obstruction freedom
Threads • A thread that executes transactions must be inherited from TMThread • Each thread can run a single transaction at a time Don’t forget the run() method class TMThread : Thread { void beginTransaction(); boolcommitTransaction(); void abortTransaction(); }
Objects (1) • All transactinal objects must implement the TMCloneable interface: • This method clones the object, but clone implementors don’t need to handle synchronization issues intefaceTMCloneable { Object clone(); }
Objects (2) • In order to make an object transactional, need to wrap it • TMObject is a container for regular Java objects TMObject Object
Opening an object • Before using a TMObject in a transaction, it must be opened • An object can either be opened for READ or WRITE (and read) class TMObject { TMObject(Object obj); enum Mode {READ, WRITE}; Object open(Mode mode); }
Overview • Short recap and what’s new? • How to use DSTM? • Example • Diving into DSTM • Example 2 • Improving performance • Obstruction freedom
An atomic counter (1) • The counter has a single data member and two operations: • The object is shared by multiple threads class Counter : TMCloneable { intcounterValue = 0; void inc(); // increment the value int value(); // returns the value Object clone(); }
An atomic counter (2) • When a thread wants to access the counter in a transaction, it must first open the object using the encapsulated version: Counter counter = new Counter(); TMObjecttranCounter = new TMObject(counter); ((TMThread)Thread.currentThread).beginTransaction(); … Counter counter = (Counter)tranCounter.open(WRITE); counter.inc(); … ((TMThread)Thread.currentThread).commitTransaction(); Returns true/false to indicate commit status
Overview • Short recap and what’s new? • How to use DSTM? • Example • Diving into DSTM • Example 2 • Improving performance • Obstruction freedom
DSTM implementation • Transactional object structure: status transaction new object start Data old object TMObject Locator Data
Current object version • The current object version is determined by the status of the transaction that most recently opened the object in WRITE mode: • committed: the new object is the current • aborted: the old object is the current • active: the old object is the current, and the new is tentative • The actual version only changes when a commit is successful
Opening an object (1) • Let's assume transaction A tries to open object o in WRITE mode. • Let transaction B be the transaction that most recently opened o in WRITE mode. • We need to distinguish between the following cases: • B is committed • B is aborted • B is active
Opening an object (2) – B committed committed transaction Data new object start old object If CAS fails, A restarts from the beginning o Data B’s Locator clone 4 Use CAS in order to replace locator active transaction new object Data old object A’s Locator 3 1 A sets old object to the previous new A creates a new Locator 2 A clones the previous new object, and sets new
Opening an object (3) – B aborted aborted transaction Data new object start old object o Data B’s Locator 4 Use CAS in order to replace locator active transaction clone new object Data old object A’s Locator 1 3 A sets old object to the previous old A creates a new Locator 2 A clones the previous old object, and sets new
Opening an object (4) – B active • Problem: B is active and can either commit or abort, so which version (old/new) should we use? • Answer: A and B are conflicting transactions, that run at the same time • Use Contention Manager to decide which should continue and which should abort • If B needs to abort, try to change its status to aborted (using CAS)
Opening an object (5) • Lets assume transaction A opens object o in READ mode • Fetch the current version just as before • Add the pair (o, v) to the readers list (read-only table)
Committing a transaction • The commit needs to do the following: • Validate the transaction • Change the transaction’s status from active to committed (using CAS)
Validating transactions • What? • Validate the objects (only) read by the transaction • Why? • To make sure that the transaction observes a consistent state • How? • For each pair (o, v) in the read-only table, verify that v is still the most recently committed version of o • Check that (status == active) If the validation fails, throw an exception so the user will restart the transaction from the beginning
Validation inconsistency • Assume two threads A and B • If B after A, then o1 = 2, o2 = 1; • If A after B, then o1 = 1, o2 = 2 • If they run concurrently we can have o1 = 1, o2 = 1 which is illegal Thread A 1. x <- read(o1) 2. w(o2, x + 1) Thread B 1. y <- read(o2) 2. w(o1, y + 1) Initially: o1 = 0 o2 = 0
Conflicts • Conflicts are detected when: • A transaction first opens an object and finds that it is open for modification by another transaction • When the transaction validates its read set (on opening an object or commit)
Overview • Short recap and what’s new? • How to use DSTM? • Example • Diving into DSTM • Example 2 • Improving performance • Obstruction freedom
Ordered Integer List – IntSet (1) Min 3 4 8 Max 6
Ordered Integer List – IntSet (2) class List implements TMCloneable { int value; TMObject next; List(int v) { value = v; } public Object clone() { List newList = new List(value); newList.next = next; return newList; } }
Ordered Integer List – IntSet (3) class IntSet { TMObject first; // the list’s anchor IntSet() { List firstList = new List (Integer.MIN_VALUE); first = new TMObject(firstList); firstList.next = new TMObject( new List(Integer.MAX_VALUE)); } }
Ordered Integer List – IntSet (4) class IntSet { boolean insert(int v) { List newList = new List(v); TMObjectnewNode = new TMObject(newList); TMThread thread = Thread.currentThread(); while (true) { thread.beginTransaction(); boolean result = true; try { … } catch (Denied d) {} if (thread.commitTransaction()) return result; } } }
Ordered Integer List – IntSet (5) try { List prevList = (List)this.first.open(WRITE); List currList = (List)prevList.next.open(WRITE); while (currList.value < v) { prevList = currList; currList = (List)currList.next.open(WRITE); } if (currList.value == v) { result = false; } else { result = true; newList.next = prevList.next; prevList.next = newNode; } }
Overview • Short recap and what’s new? • How to use DSTM? • Example • Diving into DSTM • Example 2 • Improving performance • Obstruction freedom
Single entrance • What is the problem with the previous example? • How can it be solved? • Opening for READ on traversal • Maybe something more sophisticated?
Releasing an object • An object that was open for READ can be released • What does it imply? • Careful planning • Can increase performance • What happens if we open an object, release it and open it again in the same transaction? • Can lead to validation problems
Overview • Short recap and what’s new? • How to use DSTM? • Example • Diving into DSTM • Example 2 • Improving performance • Obstruction freedom