400 likes | 532 Views
Transactional Memory Guest Lecture Design of Parallel and High-Performance Computing. Georg Ofenbeck. TexPoint fonts used in EMF. Read the TexPoint manual before you delete this box.: A A. Locking Recap: The Problem. void PushLeft ( DQueue *q, int val ) {
E N D
Transactional MemoryGuest Lecture Design of Parallel and High-Performance Computing Georg Ofenbeck TexPoint fonts used in EMF. Read the TexPoint manual before you delete this box.: AA
Locking Recap: The Problem voidPushLeft (DQueue*q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; QNode *LS = q->left; QNode *LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } • How to make this parallel? • Global lock to coarse grained • Fine grained locking not trivial • How to compose? Double queue ? ? ? ? LS 10 20 RS qn
Locking Recap: A possible Solution voidPushLeft(DQueue*q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode *LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } } • Transactional Memory might be a solution • Makes a code block behave like its executed as a single atomic instruction Double queue Transaction LS 10 20 RS qn
Historical Background • Databases accessed concurrently for decades • “Transactions” as abstraction tool • Atomic • Consistency • Isolated • Durable • Proposed for general applications already 1977 by Lomet • Transactions on memory instead of databases Picture: http://godatabase.net
Research on Transactional Memory [Number of publications] [Number of hits/10] multicore IeeeXplore
Research on Software TM (STM) • IntelC++ STM compiler extends C++ with support for STM language extensions • Microsoft STM.NET is an experimental extension of the .NET Framework • Sun/OracleDSTM2 Dynamic Software Transactional Memory Library
ACID voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode *LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } } Double queue Transaction LS 10 20 RS
AtomicityCID voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode *LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } } • Failure atomicity • Transaction only succeeds if all its parts succeed • No evidence of its execution is left behind in case of failure • Doesnot mean atomic execution Double queue Transaction LS 10 qn
AtomicityCID voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode *LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } } • Failure atomicity • Transaction only succeeds if all its parts succeed • No evidence of its execution is left behind in case of failure • Doesnot mean atomic execution Double queue Transaction LS 10 qn
AConsistencyID voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode *LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } } • Consistency • A consistent state (e.g. links ok) is preserved when transaction ends • In case of an abort satisfied by the failure atomicity Double queue Transaction LS 10 qn
ACIsolationD voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode*LN = LS->right; qn->left = LS; qn->right = LN; LS->right= qn; LN->left = qn; } } • Isolation • Transaction do not interfere with each other Transaction
ACIsolationD voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode*LN = LS->right; qn->left = LS; qn->right = LN; LS->right= qn; LN->left = qn; } } • Isolation • Transaction do not interfere with each other Double queue qn Transaction LS 10 qn
ACIDurability voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode*LN = LS->right; qn->left = LS; qn->right = LN; LS->right= qn; LN->left = qn; } } • Durability • Persisting data in the database world • Sometimes relevant for Hardware TM – persisting caches to memory Transaction
TM Design Choices • Failure Atomicity • How to restore the original state? • Version Control • Isolation • How to shield concurrent transactions from each other? • Concurrency Control • Consistency • How to detect conflicts? • Conflict Detection
TM Design Choices • Failure Atomicity • How to restore the original state? • Version Control • Isolation • How to shield concurrent thread from each other? • Concurrency Control • Consistency • How to detect conflicts? • Conflict Detection
Version Control voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode*LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } } • Eager Version Management • Write changes directly in memory and change all values in Undo Log • On success simply remove Undo Log • On failure restore original values
Version Control • Eager Version Management • Write changes directly in memory and change all values in Undo Log • On success simply remove Undo Log • On failure restore original values voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode*LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } } Undo Log QNode *LS QNode *LN NULL NULL Double queue LS 10 qn
Version Control voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode*LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } } • Lazy Version Management • Write changes into a buffer and apply at commit • Apply changes during commit if no conflict occurred • Otherwise just discard buffer • Most common
Version Control • Lazy Version Management • Write changes into a buffer and apply at commit • Apply changes during commit if no conflict occurred • Otherwise just discard buffer • Most common voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode*LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } } Buffer Double queue LS 10 qn
Version Control • Lazy Version Management • Write changes into a buffer and apply at commit • Apply changes during commit if no conflict occurred • Otherwise just discard buffer • Most common void Example () { x = 2; y = 0; atomic{ x = 3; y = x; } } Buffer x 3 ? x 2 y 0
Version Control • Redirection • Explicit by library • Implicit by compiler • Overhead! voidPushLeft (DQueue *q, intval) { QNode *qn = malloc(sizeof(QNode)); qn->val = val; do{ StartTx(); QNode *LS = ReadTx(&(q->left)); QNode *oLN = ReadTx(&(LS->left)); WriteTx(&(qn->left), LS); WriteTx(&(qn->right), oLN); WriteTx(&(LS->right), qn); WriteTx(&(oLN->left), qn); } while (!CommmitTx()); }
TM Design Choices • Failure Atomicity • How to restore the original state? • Version Control • Isolation • How to shield concurrent transactions from each other? • Concurrency Control • Consistency • How to detect conflicts? • Conflict Detection
Concurrency Control voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode*LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } } • Pessimistic • Transaction locks all variables it works on during a transaction
Concurrency Control voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode*LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } } • Pessimistic • Transaction locks all variables it works on during a transaction • Deadlocks! • Usually in combination with eager version managment Double queue qn LS 10 qn
Concurrency Control voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode*LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } } • Optimistic • System maintains multiple versions and detects conflict later
Concurrency Control voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode*LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } } • Optimistic • System maintains multiple versions and detects conflict later Double queue qn LS 10 qn
Concurrency Control voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode*LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } } • Optimistic • System maintains multiple versions and detects conflict later • Livelocks! Double queue qn LS 10 qn
Concurrency Control voidPushLeft (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode*LN = LS->right; qn->left = LS; qn->right = LN; LS->right = qn; LN->left = qn; } } • Optimistic • System maintains multiple versions and detects conflict later • Livelocks! voidPushLeft2 (DQueue *q, intval) { QNode*qn = malloc(sizeof(QNode)); qn->val = val; atomic{ QNode *LS = q->left; QNode*LN = LS->right; qn->left = LS; qn->right = LN; LN->left = qn; LS->right = qn; } }
TM Design Choices • Failure Atomicity • How to restore the original state? • Version Control • Isolation • How to shield concurrent transactions from each other? • Concurrency Control • Consistency • How to detect conflicts? • Conflict Detection
Conflict detection • Granularity • Bytes, Objects …. • How to resolve? • Control to user? (Exception like) • RetryDelayedPriorities …. • For optimistic concurrency • When to check? • Eager conflict detection? • How to detect? void Example (int input) { atomic{ x = input; y = x; } }
Nesting • Flattened Nesting • Closed Nesting • Open Nesting void Example () { x = 2; y = 0; atomic{ x = 3; y = x; atomic{ z = 3*x; w = z--; } } }
Why do we bother with locks? Source:CalinCascaval, Colin Blundell, Maged Michael, Harold W. Cain, Peng Wu, Stefanie Chiras, Siddhartha Chatterjee, 2008. Software Transactional Memory: Why Is It Only a Research Toy?.Queue 6, 5 (September 2008)
Why do we bother with locks? Source:CalinCascaval, Colin Blundell, Maged Michael, Harold W. Cain, Peng Wu, Stefanie Chiras, Siddhartha Chatterjee, 2008. Software Transactional Memory: Why Is It Only a Research Toy?.Queue 6, 5 (September 2008)
Why do we bother with locks? Source:AleksandarDragojević , Pascal Felber , Vincent Gramoli , RachidGuerraoui, Why STM can be more than a research toy, Communications of the ACM, v.54 n.4, April 2011
Why do we bother with locks? Source:FeradZyulkyarov, Vladimir Gajinov, Osman S. Unsal, Adrián Cristal, Eduard Ayguadé, Tim Harris, and Mateo Valero. 2009. Atomic quake: using transactional memory in an interactive multiplayer game server.
Why do we bother with locks? Source:FeradZyulkyarov, Vladimir Gajinov, Osman S. Unsal, Adrián Cristal, Eduard Ayguadé, Tim Harris, and Mateo Valero. 2009. Atomic quake: using transactional memory in an interactive multiplayer game server.
Why do we bother with locks? [Number of publications] [Number of hits/10] IeeeXplore
Why do we bother with locks? • IntelC++ STM compiler extends C++ with support for STM language extensions • Microsoft STM.NET is an experimental extension of the .NET Framework discontinued
Summary • TM is a nice model to tackle parallelism BUT • STM does not yield good performance • HTM often with only limited functionality and hard to instrument • Hybrids the future? • Issues not covered in this lecture • Details of conflict detection • How to handle I/O? • How to handle access from outside of the transaction? • ….
More information • Transactional Memory, 2nd Edition (Synthesis Lectures on Computer Architecture)