Some Recent Advances in Haskell
Siddharth Agarwal (Y7429)Advisor: Dr. A. Karkare
CS497
• Software transactional memory• Nested data parallelism• CUDA and Haskell
CS497
• Software transactional memory• Nested data parallelism• CUDA and Haskell
The world is multi-core
Lock-based synchronization
• Protect shared mutable state• acquire lockdo stuffrelease lock
• More complex mechanisms (semaphores, monitors) can be built on top
• Familiar to programmers• Difficult to use
Deadlocks
Two processes each hold a lock that the other one wants
Process 1Process 1 Process 2Process 2
AA BB
Priority inversion
A low-priority process holds a lock that a high-priority process needs
Low prio processLow prio process High prio processHigh prio process
LockLock
Non-composable
• Classic example: Bank account• API: two synchronized methods: credit(amount) and debit(amount)
• As long as you’re just depositing or withdrawing money, you’re fine
But what if…
• you want to transfer money from one account to another?
• account1.debit(x); account2.credit(x); causes the money to “vanish” in between
• In general, compose synchronized operations into a larger synchronized operation
Solution 1
• Add a transfer() method to the API• Requires forethought by the API designer • 3-way, 4-way, … n-way transfers?
Solution 2
• Expose the locks• Need to trust calling code to use the locks
properly• Shifts the problem one level up
Solution 3
• Add a wrapper API with its own locking• How do you make others use your wrapper?
Locks aren’t composable
• In general, it is not possible to combine two lock-based synchronized operations into a larger synchronized one
• The solutions are hacks around this basic flaw
And all we wanted to do was…
Atomically account1.debit(x); account2.credit(x);
And all we wanted to do was…
atomic { account1.debit(x); account2.credit(x);}
What if…
• We could declare sections as atomic• Retriesatomic { if (items < 0) retry; …}
Can we do this with locks?
• Limited success• Why not borrow another concept from
databases?
Transactions
• Ordered sets of operations• All-or-nothing• In databases, ACID semantics
Transactional memory
• The C and D aren’t relevant, so let’s drop them
• Atomicity and isolation• Software TM first proposed by Shavit and
Touitou (PODC’95)• Language support (Java) first looked at by
Harris and Fraser (OOPSLA’03)
Software transactional memory
Maintain a transaction log
TLog Old New
x 200 200
y 100 150
Memory Value Version
x 200 42
y 100 14
z 300 27
Software transactional memory
On read, read from or add to the logx = z - 50;
TLog Old New
x 200 200
y 100 150
Memory Value Version
x 200 42
y 100 14
z 300 27z 300 300
Software transactional memory
On write, write to the logx = z - 50;
TLog Old New
x 200 200
y 100 150
z 300 300
Memory Value Version
x 200 42
y 100 14
z 300 27
TLog Old New
x 200 250
y 100 150
z 300 300
Software transactional memory
On commit, (a) check and record version for unmodified data
TLog Old New
x 200 250
y 100 150
z 300 300
Memory Value Version
x 200 42
y 100 14
z 300 27
TLog Old New
x 200 250
y 100 150
z 300, 27 300
TLog Old New
x 200 250
y 100 150
z 300, 27 300
Software transactional memory
(b) Lock locations to be modified
Memory Value Version
x 200 42
y 100 14
z 300 27
Memory Value Version
x 200 42
y 100 14
z 300 27
Memory Value Version
x 200 42
y 100 14
z 300 27
TLog Old New
x 200 250
y 100 150
z 300, 27 300
Software transactional memory
(c) Check version numbers, then write new values back
Memory Value Version
x 250 42
y 150 14
z 300 27
Memory Value Version
x 250 42
y 150 14
z 300 27
Memory Value Version
x 250 43
y 150 15
z 300 27
TLog Old New
x 200 250
y 100 150
z 300, 27 300
Software transactional memory
(d) Increment version numbers and unlock
Hardware support needed: just CAS
Problem 1: Overhead
• Does each location need a version number?• Solutions– Hashing– Tag locations as TM
Problem 2: I/O
• atomic { launchMissiles(); if (*) retry;}
• Perform on commit– What if you need input?
• OnCommit/OnRollback operations• Forbid I/O– Dynamically or statically?
Problem 3: Weak or strong atomicity?
• Same memory location accessed both inside and outside an atomic block
• Forbid this, making the question moot?• STM.NET chose weak atomicity and in-place
updates, which led to major problems
Haskell
• Strictly typed, lazy, pure functional language
Haskell
• Strictly typed, lazy, pure functional language• Pure: Functions with side-effects must be
“marked”• Marking done through the type system
I/O in Haskell
int f(int x) { return (x + 2);}
f x = x + 2f :: Integer -> Integer
I/O in Haskellint g(int x) { System.out.println(“Hello, world!”); return (x + 2);}
g x = do { putStrLn “Hello, world!”; return (x + 2);}g :: Integer -> IO Integer
I/O in Haskellint g(int x) { System.out.println(“Hello, world!”); return (x + 2);}
g x = do putStrLn “Hello, world!” return (x + 2)
g :: Integer -> IO Integer
I/O in Haskell
• Functions with side-effects are marked at compile time
• Compiler can perform checks (e.g. forbidding I/O)
• STM is just another kind of I/O
STM in Haskell
• A different marker: STM• Transactional memory needs to be declared
explicitly as TVar• Most memory is not transactional– Most memory is not mutable!– Prevents problem 1 (overhead)
STM in Haskell
• I/O forbidden inside STM– Prevents problem 2 (I/O)– STM to IO converter called atomically
• Accesses to STM variables are through readTVar and writeTVar– They can only be called inside STM blocks– Prevents problem 3 (atomicity)
STM in Haskell is composable
• Two types of composability:– Sequential– Alternative-based
Alternative-based composition
• Provide two or more alternatives• If the first alternative retries, try the next one• Exposed through the orElse operator
Putting it all together
(Demo)
Future directions
• Transactional memory-only operations are a solved problem in Haskell
• Hook into other transaction providers– Databases– File system
Thank you
Top Related