ParaSail Parallel Specification and Implementation Language: A Pointer-Free Path to Secure Object-Oriented Parallel Programming S.
Download
Report
Transcript ParaSail Parallel Specification and Implementation Language: A Pointer-Free Path to Secure Object-Oriented Parallel Programming S.
ParaSail
Parallel Specification and
Implementation Language:
A Pointer-Free Path to
Secure Object-Oriented Parallel Programming
S. Tucker Taft
FOOL Workshop, Tuscon, AZ
October 2012
Goals of ParaSail
A simple, safe, pervasively parallel, yet familiar,
language for the multi/many-core world
Easier to write parallel algorithms than
sequential ones
Parallel by default, yet race-free
Have to work harder to get sequential execution
Not an academic exercise
More of a human engineering exercise
Presumes advanced static analysis at compile-time
AdaCore © 2012
2
Why a new language?
Computers have stopped getting faster
Courtesy IEEE
Computer,
January 2011,
page 33.
AdaCore © 2012
3
A Simplified Approach to
Secure Parallel Programming
Simplify/Unify
Smaller number of concepts, uniformly applied, all features available
to user-defined types -- generally a good thing
Simplify to make conceptual room for parallelism and formalism
Simplify to make pervasive parallelism easy to verify
Remove all the hard problems!
Parallelize
Parallel by default, every expression is parallelizable
Have to work harder to force sequential execution
Formalize
4
Assertions, Invariants, Preconditions, Postconditions integrated into
the syntax
Compiler complains if it can’t prove the assertion
No run-time exceptions, no run-time exception handling
AdaCore © 2012
ParaSail Simplifications
No pointers (the “goto” of data structuring)
No global variables
No global, garbage-collected heap
No parameter aliasing
No special syntax reserved for built-in types
No run-time exception handling
No explicit threads, lock/unlock, wait/signal
No race conditions
No algebraic data types -- use OO polymorphism
No boxing/run-time-type overhead unless explicitly
polymorphic
5
AdaCore © 2012
ParaSail Type and Value Model
OO/functional core
Types:
T ::= Tid | Tid+ | Mid <T1, T2, ...> | optional T | new T
FT ::= (Oid1 : T1; Oid2 : T2; ... func Fid1 FT1; ...) -> T3
Values:
V ::= N | L | (V) | FN (V1, V2, ... FV1, FV2, ...)
V ::= (if V1 then V2 else V3) | V is null | V not null
N ::= Oid | N.Oid | Tid :: N
// names
L ::= 42 | 3.14159 | “a string” | ‘c’ | #green
// literals
L ::= null | (Oid1 => V1, Oid2 => V2, ...)
FV ::= FN | lambda FT is (V)
// function values
6 FN ::= Fid | Tid :: FN
// function names
AdaCore © 2012
ParaSail Declarations and Modules
OO/functional core
Declarations:
D ::= type Tid is T;
D ::= func Fid FT [is (V)];
D ::= const Oid [ : T ] [ := V ];
D ::= M
Modules:
M ::= [abstract] interface Mid < Tid1 is Mid1<>, ... >
[extends Mid2] [implements Mid3<...>, ...]
is D1; D2; ... end interface Mid;
M ::= class Mid is D11; ... exports D21; ... end class Mid;
AdaCore © 2012
7
ParaSail Syntactic Sugar
Sugared Syntax
Expanded Syntax
X bin-op Y
“bin-op”(X, Y)
un-op X
“un-op”(X)
Obj.F(X, Y)
F(Obj, X, Y)
A[I]
“indexing”(A, I)
A[I..J]
“slicing”(A, “..”(I, J))
[]
“[]”()
[A, B, ...]
[] | A | B | ...
#green
#green or “from_univ”(#green)
AdaCore © 2012
8
Example: Countable_Set
interface Countable_Set <Element_Type is Countable<>> is
op "[]"() -> Countable_Set;
func Singleton(Elem : Element_Type) -> Countable_Set;
op “in”(Element_Type; Countable_Set) -> Boolean;
op “|”(Left, Right : Countable_Set) -> Countable_Set;
...
end interface Countable_Set;
class Countable_Set is
type Elem_Interval is Closed_Interval<Element_Type>;
const Items : optional AA_Tree<Elem_Interval>;
exports
op "[]"() -> Countable_Set is ((Items => []));
func Singleton(Elem : Element_Type) -> Countable_Set
is ((Items => [(Low => Elem, High => Elem)] ));
...
end class Countable_Set;
AdaCore © 2012
9
How to add Mutability?
Why Pointer Free?
Consider F(X) + G(Y) ...
We want to be able to safely evaluate F(X) and G(Y) in parallel
without looking inside of F or G
Presume X and/or Y might be incoming var (in-out) parameters to the
enclosing operation
No global variables is clearly pretty helpful
Otherwise F and G might be stepping on same object
No parameter aliasing is important, so we know X and Y do not refer
to the same object; use hand-off semantics
What do we do if X and Y are pointers?
10
Without more information, we must presume that from X and Y you
could reach a common object Z
Parameter modes (in-out vs. in, var vs. non-var) don’t help with objects
accessible via pointers; need a more elaborate “permission” system
AdaCore © 2012
ParaSail types, values, declarations
with mutable objects
Types and Values with mutable objects:
T ::= ... [as before]
FT ::= ( [locked | queued] [var] Oid1 : T1; ... func Fid1 FT1; ...)
[ -> [Oid3 : ] T3 ]
V ::= ... [as before]
Declarations and Modules with mutable objects:
D ::= type Tid is T;
D ::= func Fid FT [is (V) | is S];
D ::= (const | var) Oid [ : T ] [ := V ];
D ::= M
M ::= [abstract] [concurrent] interface ... [as before]
M ::= [concurrent] class ... [as before]
11
AdaCore © 2012
ParaSail imperative statements
preserve value semantics
S ::= D
// declarations interspersed
S ::= N := V | N1 <== N2 | N1 <=> N2 // copy, move, swap
S ::= FN (V1, V2, ... FV1, FV2, ...) // call for effects on params
S ::= if V then S1 else S2 end if
S ::= block S end block
S ::= [while V] loop S end loop
S ::= exit (loop | block)
S ::= return [ V ]
// or may assign to named output
S ::= S1 ; S2
// sequential, but OK to parallelize
S ::= S1 || S2
// parallel; error if data dependence
S ::= S1 then S2
// force sequential
12
AdaCore © 2012
Expandable Mutable Objects
Instead of Pointers
All types have additional null value; objects can be
declared optional (i.e.null is OK) and can grow and shrink
Eliminates many of the common uses for pointers, e.g. trees
Assignment (“:=“) is by copy
Move (“<==“) and swap (“<=>”) operators also provided
Generalized indexing into containers replaces pointers for
cyclic structures
for each N in Directed_Graph[I].Successors loop ...
Region-Based Storage Mgmt can replace Global Heap
All objects are local with growth/shrinkage using local heap
null value carries indication of region to use on growth
Short-lived references to existing objects are permitted
13
Returned by user-defined indexing functions, for example
AdaCore © 2012
Mutable Pointer-Free Trees
interface Tree_Node<Payload_Type is Assignable> is
var Payload : Payload_Type;
var Left : optional Tree_Node := null; // defaults to null
var Right : optional Tree_Node := null; // defaults to null
end interface Tree_Node;
var Root : Tree_Node<Univ_String> :=
(Payload => “Top”, Left => null, Right => null);
Root.Left := (Payload => “L”, Right => (Payload => “LR”));
Root.Right <== Root.Left.Right; // Root.Left.Right now null
AdaCore © 2012
14
Example: Mutable Countable_Set
interface Countable_Set <Element_Type is Countable<>> is
op "[]"() -> Countable_Set;
op “|=“(var S : Countable_Set; Elem : Element_Type);
...
end interface Countable_Set;
class Countable_Set is
type Elem_Interval is Closed_Interval<Element_Type>;
var Items : optional AA_Tree<Elem_Interval>;
exports
op "[]"() -> Countable_Set is
return (Items => []);
end op "[]";
op "|="(var S : Countable_Set; Elem : Element_Type) is
const IV : Elem_Interval := (Low => Elem, High => Elem);
if not Overlapping(S.Items, IV) then
S.Items |= IV;
end if;
end op "|=";
AdaCore © 2012
15 ...
Region-Based Storage Management
Pioneered by Tofte and Talpin
Refined in Cyclone language
Region (i.e. local heap) in each scope
Space allocated from, and returned to, associated region
when expanding and shrinking object
null value identifies region from which to allocate
No garbage accumulates; no asynchronous collector
Region reclaimed in full on exiting scope
effectively => stack-based heap management
Move (“<==“) and swap (“<=>”) very cheap when staying
within region (analogous to “mv” in Unix)
Can declare object and give “hint” to where it will be moved:
var X for Root : Payload_Type := ...
16
Root.Left.Payload <== X;
AdaCore © 2012
Stack of Regions
with Region Chunks
Regions
Region Chunks
Object
handles
(no chunks)
AdaCore © 2012
17
Regions in a Parallel Programming
Language
Global garbage-collected heap bad news in parallel
language
Global lock on allocation is serious bottleneck
Concurrent threads allocate unrelated objects in neighboring locations
Individual object has storage spread all over memory
Worst of both worlds (well, all 3 really)
Region provides attractive alternative
Each region can have its own lock
Concurrent threads are allocating in different regions
Individual objects are concentrated in a single region
Better in all dimensions
AdaCore © 2012
18
Overall ParaSail Model
ParaSail has four basic concepts:
Module
Type
is an instance of a Type
var Obj : T := Create(...);
mutable value semantics
Operation
19
is an instance of a Module
type T is [new] M <Actual>;
“T+” is polymorphic type for types inheriting from T’s interface
Object
has an Interface, and Classes that implement it
interface M <Formal is Int<>> is ...
Supports inheritance of interface and code
is defined in a Module, and
operates on one or more Objects of specified Types.
are visible automatically based on types of parameters/result
AdaCore © 2012
Expression and Statement
Parallelism
Within an expression, parameters/operands to an
operation are evaluated in parallel
Max ( F(X), G(Y) * H(Z) )
F(X), G(Y), H(Z) evaluated concurrently
Programmer can force parallelism across statements
P(X) || Q(Y)
Programmer can force sequentiality
P(X) then Q(Y)
Default is run in parallel if no dependences
A := P(X); B := Q(Y) -- can run in parallel
Y := P(X); B := Q(Y) -- cannot run in parallel
Work stealing used to schedule parallel computations
AdaCore © 2012
20
Example: Recursive Parallel
Word Count
func Word_Count
(S : Univ_String; Separators : Countable_Set<Univ_Character> := [' '])
-> Univ_Integer is
// Return count of words separated by given set of separators
case Length(S) of
[0] => return 0; // Empty string
[1] =>
if S[1] in Separators then
return 0; // A single separator
else
return 1; // A single non-separator
end if;
[..] =>
// Multi-character string; divide and conquer
const Half_Len := Length(S)/2;
const Sum := Word_Count(S[1 .. Half_Len], Separators) +
Word_Count(S[Half_Len <.. Length(S)], Separators);
if S[Half_Len] in Separators
or else S[Half_Len+1] in Separators then
return Sum;
// At least one separator at border
else
return Sum-1; // Combine words at border
end if;
end case;
end func Word_Count;
AdaCore © 2012
21
More Examples of ParaSail
Parallelism
for X => Root then X.Left || X.Right while X not null
concurrent loop
Process(X.Data);
// Process called on each node in parallel
end loop;
concurrent interface Box<Element is Assignable<>> is
func Create() -> Box;
// Creates an empty box
func Put(queued var B : Box; E : Element); // waits til empty
func Get(queued var B : Box) -> Element;
// waits til full
func Get_Now(locked B : Box) -> optional Element;
end interface Box;
type Item_Box is Box<Item>;
var My_Box : Item_Box := Create();
AdaCore © 2012
22
Synchronizing ParaSail Parallelism
concurrent class Box <Element is Assignable<>> is
var Content : optional Element; // starts out null
exports
func Create() -> Box is // Creates an empty box
return (Content => null);
end func Create;
func Put(queued var B : Box; E : Element) is
queued until B.Content is null then
B.Content := E;
end func Put;
// waits until empty
func Get(queued var B : Box) -> Result : Element is // waits until full
queued until B.Content not null then
Result <== B.Content; // “move” sets B.Content to null as side-effect
end func Get;
func Get_Now(locked B : Box) -> optional Element is
return B.Content;
end func Get_Now;
end class Box;
AdaCore © 2012
23
ParaSail Yin and Yang
Race-Free Parallel Programming
Mutable Value Semantics
Stack-Based Heap Management
Compile-Time Exception Handling
AdaCore © 2012
24
Conclusions and Ongoing Work
Simplified, pointer-free type model can still be flexible
safe by construction
Can support new capabilities
pervasive parallelism
integrated annotations checked at compile-time
Ongoing ParaSail work
Finalizing language, interpreter, and work-stealing scheduler
Integrating language with IDE
Building backend to generate compilable C, Ada, LLVM Assembler
Working With Microsoft Research: ParaSail + Dafny
Read the blog and download the prototype ...
http://parasail-programming-language.blogspot.com
25
AdaCore © 2012
AdaCore
24 Muzzey Street
Lexington, MA 02421
Tucker Taft
[email protected]
http://parasail-programming-language.blogspot.com
+1 (781) 750-8068 x220
AdaCore © 2012
26