Task Types for Pervasive Atomicity Aditya Kulkarni, Yu David Liu - - PowerPoint PPT Presentation

task types for pervasive atomicity
SMART_READER_LITE
LIVE PREVIEW

Task Types for Pervasive Atomicity Aditya Kulkarni, Yu David Liu - - PowerPoint PPT Presentation

Task Types for Pervasive Atomicity Aditya Kulkarni, Yu David Liu State University of New York at Binghamton & Scott Smith Johns Hopkins University October 2010 @OOPSLA Task Types for Pervasive Atomicity Aditya Kulkarni, Yu David Liu State


slide-1
SLIDE 1

Task Types for Pervasive Atomicity

Aditya Kulkarni, Yu David Liu State University of New York at Binghamton & Scott Smith Johns Hopkins University October 2010 @OOPSLA

slide-2
SLIDE 2

Task Types for Pervasive Atomicity

Aditya Kulkarni, Yu David Liu State University of New York at Binghamton & Scott Smith Johns Hopkins University October 2010 @OOPSLA

slide-3
SLIDE 3

Task Types for Pervasive Atomicity

Aditya Kulkarni, Yu David Liu State University of New York at Binghamton & Scott Smith Johns Hopkins University October 2010 @OOPSLA

slide-4
SLIDE 4

Task Types for Pervasive Atomicity

Aditya Kulkarni, Yu David Liu State University of New York at Binghamton & Scott Smith Johns Hopkins University October 2010 @OOPSLA

slide-5
SLIDE 5

Atomicity Atomicity: a piece of code may interleave with others, but always behaves as if no interleaving happened

¬ Important for program understanding and

analysis for multi-core software

A lot of existing work on implementation strategies:

¬ pessimistic lock-based ¬ optimistic transaction-based (STM, HTM)

This talk largely independent on the choices

  • f implementation strategies
slide-6
SLIDE 6

Atomic Blocks: Deceptively Simple

class Bank { class Bank { … … void transfer (Account from, Account to, void transfer (Account from, Account to, int int amount) { amount) { … … atomic { atomic { from.decrease(amount from.decrease(amount); ); to.increase(amount to.increase(amount); ); } } } } } }

slide-7
SLIDE 7

Atomic Blocks: Atomicity Zones as Islands

thread 1 thread 2 atomic execution non-atomic execution execution interleaving OK!

slide-8
SLIDE 8

class Bank { class Bank { … … void transfer (Account from, Account to, void transfer (Account from, Account to, int int amount) { amount) { … … atomic { atomic { from.decrease(amount from.decrease(amount); ); to.increase(amount to.increase(amount); ); } } } } void deposit (Account acc, void deposit (Account acc, int int amount) { amount) { acc.increase(amount acc.increase(amount); ); } } } }

The Problems of Atomic Blocks (I)

slide-9
SLIDE 9

thread 1 thread 2 atomic execution non-atomic execution execution interleaving OK?

The Problems of Atomic Blocks (I)

weak atomicity -> strong atomicity

slide-10
SLIDE 10

thread 1 thread 2 atomic execution non-atomic execution execution What’s programmer’s intention here?

The Problems of Atomic Blocks (II)

slide-11
SLIDE 11

What’s programmer’s intention here?

The Problems of Atomic Blocks (II)

5 lines 50 lines “After carefully perusing 45 lines of code, I decide they are harmless to be outside atomic blocks, because: 1) they never involve in interleaving, or 2) interleaving never changes their observable behavior, or 3) interleaving changes their behavior that I expect”

slide-12
SLIDE 12

What’s programmer’s intention here?

The Problems of Atomic Blocks (II)

5 lines 500,000 lines “After carefully perusing 499,995 lines of code, I decide they are harmless to be outside atomic blocks, because: 1) they never involve in interleaving, or 2) interleaving never changes their observable behavior, or 3) interleaving changes their behavior that I expect”

slide-13
SLIDE 13

The Problems of Atomic Blocks

thread 1 thread 2 atomic execution non-atomic execution execution perhaps this island-building language abstraction should be abandoned

slide-14
SLIDE 14

Let’s Be Audacious

thread 1 thread 2 atomic execution execution

slide-15
SLIDE 15

You Ask…

Question 1: wouldn’t threads accessing “exclusive resources” end up waiting each other for a long time (or rolling back a lot)? Question 2: familiar Java-like communication/sharing patterns, such as rendezvous? Question 3: “pervasive atomicity”??? You mean “pervasive run-time locks/transactions?

slide-16
SLIDE 16

Atomicity Break Points

thread 1 thread 2 atomic execution execution atomicity breaking point pervasive atomicity: every line of code still lives in SOME atomic zone!

slide-17
SLIDE 17

You Ask…

Question 1: wouldn’t threads accessing “exclusive resources” end up waiting each other for a long time? (or rolling back a lot) Question 2: : familiar Java-like communication/ sharing patterns, such as rendezvous? Question 3: “pervasive atomicity”??? You mean “pervasive run-time locks/transactions?

slide-18
SLIDE 18

Shared Access as Atomicity Break Points

thread 1 thread 2 atomic execution execution atomicity breaking point start shared access end shared access

slide-19
SLIDE 19

You Ask…

Question 1: wouldn’t threads accessing “exclusive resources” end up waiting each other for a long time (or rolling back a lot)? Question 2: : familiar Java-like communication/ sharing patterns, such as rendezvous? Question 3: “pervasive atomicity”??? You mean “pervasive run-time locks/transactions?

Task Types, a type system that overlays a non-shared-memory-by-default model on top of the Java-like shared memory

slide-20
SLIDE 20

The Language Design

slide-21
SLIDE 21

This Talk

A Java-like programming language, Coqa (first appeared in CC’08), with

¬ pervasive atomicity:

v Benefits: the scenarios of interleaving are significantly

reduced by language design, hence promoting better programming understanding and easier bug detection

¬ sharing-aware programming ¬ a static type system to enforce non-shared-memory-

by-default

slide-22
SLIDE 22

Example I

class Main { class Main { void main() { void main() { (new Person())->eat(); (new Person())->eat(); (new Person())->eat(); (new Person())->eat(); } } } } class Cheese { class Cheese { int int c; ; void move() { void move() { c--; }

  • -; }

} } task class Person { task class Person { void eat () { void eat () { (new (new Cheese()).move Cheese()).move(); (); } } } }

slide-23
SLIDE 23

Example I

class Main { class Main { void main() { void main() { (new Person()) (new Person())->

  • >eat();

eat(); (new Person()) (new Person())->

  • >eat();

eat(); } } } } class Cheese { class Cheese { int int c; ; void move() { void move() { c--; }

  • -; }

} } task task class Person { class Person { void eat () { void eat () { (new (new Cheese()).move Cheese()).move(); (); } } } }

A “task” is a logical thread preserving pervasive atomicity (created by sending a -> message to a “task object”)

slide-24
SLIDE 24

Example I

class Main { class Main { void main() { void main() { (new Person())->eat(); (new Person())->eat(); (new Person())->eat(); (new Person())->eat(); } } } } class Cheese { class Cheese { int int c; ; void move() { void move() { c--; }

  • -; }

} } task task class Person { class Person { void eat () { void eat () { ( (new new Cheese() Cheese()).move move(); (); } } } }

The inversion of Java’s default – all classes without any modifiers are statically enforced task-local objects (“ordinary objects”) The two “eat” tasks are atomic: no surprise such as “Who moved my Cheese?”

slide-25
SLIDE 25

Benefits of Static Isolation

Access to them does not break atomicity Access to them does not need runtime protection Static semantics gives programmers confidence that pervasive atomicity does not translate to pervasive runtime overhead

slide-26
SLIDE 26

Types of Coqa Objects

task units (“accessor”) data (“accessee”) default “shared” task objects statically isolated

  • bjects

shared task

  • bjects

dynamically isolated

  • bjects
slide-27
SLIDE 27

Task Types

Task Types: static locality/non-shared-memory enforcement for ordinary objects Can be viewed as a region/ownership type system where ordinary objects are assigned to regions – the static representation of tasks – but with unique challenges

slide-28
SLIDE 28

Parametric Polymorphic Locality Typing

class Main { class Main { void main() { void main() { (new Person())->eat(); (new Person())->eat(); (new Person())->eat(); (new Person())->eat(); } } } } class Cheese { class Cheese { int int c; ; void move() { void move() { c--; }

  • -; }

} } task class Person { task class Person { void eat () { void eat () { (new (new Cheese()).move Cheese()).move(); (); } } } }

type variable t1 type variable t2 type variable t3 (for call site at t1) type variable t4 (for call site at t2)

parametric polymorphic type inference / context sensitivity

t1 accesses t3 t2 accesses t4

slide-29
SLIDE 29

(Oversimplified) Static Access Graph

t1 t2 t3 t4

OK every type variable for ordinary objects has to commit to one region/owner

slide-30
SLIDE 30

Example II

class Main { class Main { void main() { void main() { Cheese Cheese c = new Cheese(); = new Cheese(); (new Person())-> (new Person())->eat(c eat(c); ); (new Person())-> (new Person())->eat(c eat(c); ); } } } } class Cheese { class Cheese { int int c; ; void move() { void move() { c--; }

  • -; }

} } task class Person { task class Person { void eat (Cheese void eat (Cheese c) { ) { c.move c.move(); (); } } } }

type variable t1 type variable t2 type variable t3 t1 accesses t3 t2 accesses t3

slide-31
SLIDE 31

(Oversimplified) Static Access Graph

t1 t2

Rejected

t3

slide-32
SLIDE 32

Design Challenges of Task Types

Full inference - no need to declare region-type-like parameterized classes and parametric types The number of regions (tasks) cannot be bound statically Complexity in the presence of explicit sharing

slide-33
SLIDE 33

Design Challenges of Task Types

Full inference – no need to declare region-type-like parameterized classes and parametric types The number of regions (tasks) cannot be bound statically Complexity in the presence of explicit sharing

Task Types in this light are a polymorphic region inference algorithm with instantiation-site polymorphism and method invocation-site polymorphism

slide-34
SLIDE 34

Design Challenges of Task Types

Full inference – no need to declare region-type-like parameterized classes and parametric types The number of regions (tasks) cannot be bound statically Complexity in the presence of explicit sharing

preserving soundness is not a trivial issue in presence of recursion: can’t directly borrow from region/ownership types

slide-35
SLIDE 35

Example III

class Main { class Main { void main() { void main() { Cheese Cheese c = new Cheese(); = new Cheese(); loop { loop { (new Person())-> (new Person())->eat(c eat(c); ); } } } } } } class Cheese { class Cheese { int int c; ; void move() { void move() { c--; }

  • -; }

} } task class Person { task class Person { void eat (Cheese void eat (Cheese c) { ) { c.move c.move(); (); } } } }

type variable t1 type variable t3 t1 accesses t3

slide-36
SLIDE 36

(Oversimplified) Static Access Graph

t1 t3

OK?

slide-37
SLIDE 37

Task Twinning

For each instantiation site of task objects, create a pair of type variables

¬ Goal: to mimic the loss of information in (potentially)

recursive contexts

slide-38
SLIDE 38

Previous Example III

class Main { class Main { void main() { void main() { Cheese Cheese c = new Cheese(); = new Cheese(); loop { loop { (new Person())-> (new Person())->eat(c eat(c); ); } } } } } } class Cheese { class Cheese { int int c; ; void move() { void move() { c--; }

  • -; }

} } task class Person { task class Person { void eat (Cheese void eat (Cheese c) { ) { c.move c.move(); (); } } } }

type variables t1, t2 type variable t3 t1 accesses t3

slide-39
SLIDE 39

Static Access Graph for Ex. III

t1 t3

Rejected

t2

slide-40
SLIDE 40

Previous Example II

class Main { class Main { void main() { void main() { Cheese Cheese c = new Cheese(); = new Cheese(); (new Person())-> (new Person())->eat(c eat(c); ); (new Person())-> (new Person())->eat(c eat(c); ); } } } } class Cheese { class Cheese { int int c; ; void move() { void move() { c--; }

  • -; }

} } task class Person { task class Person { void eat (Cheese void eat (Cheese c) { ) { c.move c.move(); (); } } } }

type variables t1, t1’ type variable t2, t2’ type variable t3 t1 accesses t3 t1’ accesses t3 t2 accesses t3 t2’ accesses t3

slide-41
SLIDE 41

Static Access Graph for Ex. II

t1 t2’

Rejected

t3 t1’ t2

slide-42
SLIDE 42

Design Issues for Task Twinning

Why two are enough? Wouldn’t twinning make every program fail to typecheck? Optimizations?

slide-43
SLIDE 43

Design Issues for Task Twinning

Why two are enough? Wouldn’t twinning make every program fail to typecheck? Optimizations?

slide-44
SLIDE 44

Design Issues for Task Twinning

Why two are enough? Wouldn’t twinning make every program fail to typecheck? Optimizations?

a conflict (compile-time type error) only needs two accesses to form

slide-45
SLIDE 45

Design Issues for Task Twinning

Why two are enough? Wouldn’t twinning make every program fail to typecheck? Optimizations?

slide-46
SLIDE 46

Previous Example I

class Main { class Main { void main() { void main() { (new Person())->eat(); (new Person())->eat(); (new Person())->eat(); (new Person())->eat(); } } } } class Cheese { class Cheese { int int c; ; void move() { void move() { c--; }

  • -; }

} } task class Person { task class Person { void eat () { void eat () { (new (new Cheese()).move Cheese()).move(); (); } } } }

type variable t1, t1’ type variable t2’, t2’ type variable t3 (for call site at t1) type variable t3’ (for call site at t1’) type variable t4 (for call site at t2) type variable t4’ (for call site at t2’) t1 accesses t3 t1’ accesses t3’ t2 accesses t4 t2’ accesses t4’

slide-47
SLIDE 47

Static Access Graph for Ex. I

t1 t2’ t3 t4’

OK

t1’ t3’ t4 t2

slide-48
SLIDE 48

Design Issues for Task Twinning

Why two are enough? Wouldn’t twinning make every program fail to typecheck? Optimizations?

  • 1. differentiate read/write access
  • 2. No twinning in non-recursive contexts
  • 3. …
slide-49
SLIDE 49

Design Challenges of Task Types

Full inference – no need to declare region-type-like parameterized classes and parametric types The number of regions (tasks) cannot be bound statically Complexity in the presence of explicit sharing

slide-50
SLIDE 50

Sharing-Aware Programming

class Main { class Main { void main() { void main() { Library Library l = new Library(); = new Library(); (new Student())-> (new Student())->visit(l visit(l); ); (new Student())-> (new Student())->visit(l visit(l); ); } } } } class Counter { class Counter { int int c; ; void inc() { void inc() { c++; } ++; } } } shared task shared task class Library { class Library { Counter Counter c = new Counter(); = new Counter(); void void breturn breturn() { () { c.inc c.inc(); (); } } } } task class Student { task class Student { void visit (Library void visit (Library l) { ) { … /* do stuff 1 */ … /* do stuff 1 */ l !-> !->breturn breturn(); (); … /* do stuff 2 */ … /* do stuff 2 */ } } } }

a shared resource wrapped up into an atomic execution of its own (created by sending a !-> message to a “shared task object”)

slide-51
SLIDE 51

Types of Coqa Objects

task units (“accessor”) data (“accessee”) default “shared” task objects statically isolated

  • bjects

shared task

  • bjects

dynamically isolated

  • bjects
slide-52
SLIDE 52

Shared Access as Atomicity Break Points

thread 1 thread 2 atomic execution execution atomicity breaking point start shared access end shared access

slide-53
SLIDE 53

Shared Access as Atomicity Break Points

task “visit” of second “Student” atomic execution execution atomicity breaking point start breturn breturn end breturn breturn task “visit” of first “Student”

slide-54
SLIDE 54

Shared Tasks

A programmer’s view:

¬ an encapsulation of “a shared service” with independent

lifecycle of evolution

¬ the message sender object “gets the grip of its life”

but still lets the world it interacts to evolve

Designs:

¬ Access dynamically protected: one message at a time ¬ The sender de facto triggers a synchronous subroutine

call

slide-55
SLIDE 55

Types of Coqa Objects

task units (“accessor”) data (“accessee”) default “shared” task objects statically isolated

  • bjects

shared task

  • bjects

dynamically isolated

  • bjects

+ programmability + performance

  • atomicity
slide-56
SLIDE 56

Leftover Cheese as an Example

Transfer as an example: one Person task object plans to eat the Cheese object, and then give the leftover to another Person task object to eat

¬ Can’t declare Cheese as a “shared task” ¬ Can’t declare Cheese as a default ordinary object

slide-57
SLIDE 57

Example II

class Main { class Main { void main() { void main() { Cheese Cheese c = new Cheese(); = new Cheese(); (new Person())-> (new Person())->eat(c eat(c); ); (new Person())-> (new Person())->eat(c eat(c); ); } } } } class Cheese { class Cheese { int int c; ; void move() { void move() { c--; }

  • -; }

} } task class Person { task class Person { void eat (Cheese void eat (Cheese c) { ) { c.move c.move(); (); } } } }

slide-58
SLIDE 58

Static Access Graph for Ex. II

t1 t2’

Rejected

t3 t1’ t2

slide-59
SLIDE 59

Example II Modified

class Main { class Main { void main() { void main() { Cheese Cheese c = new Cheese(); = new Cheese(); (new (new Preson Preson())-> ())->eat(c eat(c); ); (new Person())-> (new Person())->eat(c eat(c); ); } } } } shared shared class Cheese { class Cheese { int int c; ; void move() { void move() { c--; }

  • -; }

} } task class Person { task class Person { void eat (Cheese void eat (Cheese c) { ) { c!. !.move move(); (); } } } }

dynamic isolated objects (created by sending a !. message to a “shared ordinary object”)

They are in fact good old Java objects

slide-60
SLIDE 60

Static Access Graph Modified

t1 t2’

OK

t3 t1’ t2

slide-61
SLIDE 61

Types of Coqa Objects

task units (“accessor”) data (“accessee”) default “shared” task objects statically isolated

  • bjects

shared task

  • bjects

dynamically isolated

  • bjects

+ programmability + performance

  • atomicity

+ programmability

  • performance

+ atomicity

slide-62
SLIDE 62

Dynamically Isolated Ordinary Objects

Can be optimized to be statically protected in many cases, e.g. with flow-sensitive analyses, uniqueness, linear types, temporality enforcement Static approaches are always conservative: so there is a reason this style of objects stand as a separate category

slide-63
SLIDE 63

Results

slide-64
SLIDE 64

Meta-Theory Static Isolation Type soundness proved via subject reduction and progress No race conditions Pervasive atomicity enforcement

slide-65
SLIDE 65

Static Isolation

For every default

  • rdinary object, there

must be a cut vertex on the graph OK

slide-66
SLIDE 66

Meta-Theory Static Isolation Type soundness proved via subject reduction and progress No race conditions Pervasive atomicity enforcement

slide-67
SLIDE 67

Implementation Coqa with Task Types: implemented on top of Polyglot

Most Java features except native code and reflection Lock-based semantics Non-exclusive read lock and exclusive write locks

¬ Subsumes “access to immutable objects does not lead to

atomicity violation”

Deadlocks still possible

In a non-shared-memory-by-default model, deadlocks are relatively uncommon – no locks no deadlocks!

slide-68
SLIDE 68

Initial Case Studies Benchmarks:

« An “embarrassingly parallel” Raytracer « A contention-intensive Puzzlesolver

Results:

« programmability: the syntactical “diff” between

Java and Coqa is minimal: only the new class modifiers and invocation symbols

« Performance on a 24-core machine:

« 15-35% faster than purely dynamically enforced

atomicity

« 5-35% slower than correctly synchronized but no

atomicity Java

slide-69
SLIDE 69

Some Related Work

Actors, actor-like languages, actor-inspired languages

« We (roughly) belong to this category, with a

focus on minimal change of Java programmer habits, atomicity, and static isolation Language designs for atomicity, esp. AME, “yield” by Yi & Flanagan, data-centric atomicity Determinism by design Static thread locality: escape analysis, type-based isolation Talks in this session!

slide-70
SLIDE 70

Concluding Remarks

Pervasive atomicity addresses the need of writing software on multi-core platforms, where interleavings are pervasive Enforcing pervasive atomicity with non-shared- memory-as-default achieves efficiency and better program understanding Non-shared-memory-by-default can be enforced by a static type system Sharing-aware programming helps retain coding habits familiar with Java programmers, with increased declarativity

slide-71
SLIDE 71

Thank you!

slide-72
SLIDE 72

Typing Shared Task Access

class Main { class Main { void main() { void main() { Library Library l = new Library(); = new Library(); (new Student())-> (new Student())->visit(l visit(l); ); (new Student())-> (new Student())->visit(l visit(l); ); } } } } class Counter { class Counter { int int c; ; void inc() { void inc() { c++; } ++; } } } shared task class Library { shared task class Library { Counter Counter c = new Counter(); = new Counter(); void void breturn breturn() { () { c.inc c.inc(); (); } } } } task class Student { task class Student { void visit (Library void visit (Library l) { ) { l !-> !->breturn breturn(); (); } } } }

type variable t1, t1’ type variable t2, t2’ type variable t3, t3’ type variable t4 for t1 type variable t4’ for t1’

slide-73
SLIDE 73

Static Access Graph

t2 t3’ t1 t4

OK

t2’ t3 t1’ t4’

slide-74
SLIDE 74

More Access Graphs

OK!

slide-75
SLIDE 75

More Access Graphs

OK!