i ) { intN [ i ] = new i n t [N] ( T o o l k i t . randomIntArray ( i + 1 ) ) ; T o o l k i t . output ( intN [ i ] ) ; }
22 23 24 25 26
FT
27 28 29 30 31
33 34 35 36 37 38 39 40
/ / c o m p u t e s t h e r e f e r e n c e and c o m p a r e s i n t [TOTAL] r e f e r e n c e = new i n t [TOTAL ] ; f o r ( i n t i ) { f o r ( i n t
j ) { r e f e r e n c e [ i ∗ P + j ] = intN [ j ] [ i ] ; } } s i n k . compareWith ( r e f e r e n c e ) ;
41 42 43 44 45 46 47 48
50
}
D
}
49
51
RA
/ / computes th e r e s u l t ValueArraySink < i n t , TOTAL> s i n k = new ValueArraySink < i n t , TOTAL> ( ) ; var t = ( [ G e n e r i c P a t t e r n s . < i n t , P> p a r a l l e l G e n e r a t o r ( Weave.
i n i t ( ) ) ] ) => t a s k s i n k . dump ; Timer t i m e r = new Timer ( ) ; timer . s t a r t ( ) ; t . finish () ; t i m e r . s to p ( ) ;
32
In a map pattern, a function is applied to all elements of a collection, usually producing a new collection with the same shape as the input, and the elementary function must be pure (without side effects) [McCool et al., 2012].
1 2 3 4 5
6 7
/ ∗ map p a t t e r n ∗ /
/∗ b a s i c version , i d e n t i c a l to input ∗ F : t h e map f i l t e r ∗ / public t a s k s t a t i c l o c a l IsoTask map( IsoTask F ) { return F ; }
8 9 10 11
/∗ p a r a l l e l version ∗ PP : t h e number o f p a r a l l e l s u b t a s k s ∗ F : t h e map f i l t e r ∗ /
11
12
13 14
public t a s k s t a t i c l o c a l IsoTask m a p P a r a l l e l ( IsoTask F ) { r e t u r n G e n e r i c P a t t e r n s . < TI , TO, PP> f o r k j o i n ( F ) ; }
15 16 17 18 19
20 21 22 23 24 25
/∗ array version ∗ NN: i n p u t a r r a y l e n g t h ∗ F : t h e map f i l t e r ∗ / public t a s k s t a t i c l o c a l IsoTask mapArray ( IsoTask F ) { r e t u r n ( TI [ [NN] ] # TI ) / / TI => F / / TO => (TO # TO [ [NN] ] ) ; }
29 30 31
32 33 34 35 36 37
/ ∗ p a r a l l e l v e r s i o n on v a l u e a r r a y ∗ NN: i n p u t a r r a y l e n g t h ∗ PP : t h e number o f p a r a l l e l s u b t a s k s ∗ F : t h e map f i l t e r ∗ / public t a s k s t a t i c l o c a l IsoTask mapArrayParallel ( IsoTask F ) { r e t u r n ( TI [ [NN] ] # TI ) / / TI => G e n e r i c P a t t e r n s . < TI , TO, PP> f o r k j o i n ( F ) / / TO => (TO # TO [ [NN] ] ) ; }
RA
27 28
FT
26
U SED IN: src/common/GenericPatterns.lime on page 1
The following program tests MapParallel:
2 3 4 5 6 7 8 9 10 11 12 13 14
package t e s t c a s e s . map ;
import common . ∗ ; import lime . u t i l . Tasks ; public c l a s s I n c {
D
1
/ ∗ a p p l y s t a s k i n c t o an i n t e g e r a r r a y ∗ / public s t a t i c void main ( S t r i n g [ ] a r g s ) { System . out . p r i n t l n ( I n c . c l a s s . getName ( ) ) ; final int N = 8 , P = 4; i n t [ [N] ] intN = T o o l k i t . randomIntArray ( System . nanoTime ( ) ) ; System . out . p r i n t ( T o o l k i t . INPUT ) ; T o o l k i t . < i n t , N>output ( intN ) ;
15 16 17 18 19 20 21 22 23 24
/ / computes th e r e s u l t ValueArraySink < i n t , N> s i n k = new ValueArraySink < i n t , N> ( ) ; var t = Tasks . s o u r c e ( intN ) => ( [ G e n e r i c P a t t e r n s . < i n t , i n t , P> m a p P a r a l l e l ( t a s k T o o l k i t . i n c ) ] ) => t a s k s i n k . dump ; Timer t i m e r = new Timer ( ) ; timer . s t a r t ( ) ; t . finish () ; t i m e r . s to p ( ) ;
12
25
/ / c o m p u t e s t h e r e f e r e n c e and c o m p a r e s i n t [N] r e f e r e n c e = new i n t [N] ( intN ) ; f o r ( i n t i ) { reference [ i ] = Toolkit . inc ( reference [ i ] ) ; } s i n k . compareWith ( r e f e r e n c e ) ;
26 27 28 29 30 31
}
32 33 34
}
A recurrence is like a map but where elements can use the outputs of adjacent elements as inputs [McCool et al., 2012]. Here we show how iteration can be done in this style. 1
/∗ recurrence pattern
4 5
6 7 8 9 10 11 12 13 14 15
/ ∗ NN: number o f i t e r a t i o n s ∗ F : the i t e r a t i v e operation ∗/ public t a s k s t a t i c l o c a l IsoTask r e c u r r e n c e ( IsoTask F ) { i f (NN. s i z e > 1 ) { f i n a l i n t SUB = NN. s i z e − 1 ; return F // T => G e n e r i c P a t t e r n s . r e c u r r e n c e ( F ) ; } else { return F ; } }
RA
3
FT
2
U SED IN: src/common/GenericPatterns.lime on page 1
Here is a test case that increases integer values multiple times:
2 3 4 5 6 7 8 9 10 11 12 13 14
package t e s t c a s e s . r e c u r r e n c e ; import common . ∗ ; import lime . u t i l . Tasks ;
D
1
public c l a s s I n c {
/ ∗ a p p l y s t a s k i n c t o an i n t e g e r v a l u e a r r a y m u l t i p l e t i m e s ∗ / public s t a t i c void main ( S t r i n g [ ] a r g s ) { System . out . p r i n t l n ( I n c . c l a s s . getName ( ) ) ; final int N = 8 , R = 4; i n t [ [N] ] intN = T o o l k i t . randomIntArray ( System . nanoTime ( ) ) ; System . out . p r i n t ( T o o l k i t . INPUT ) ; T o o l k i t . < i n t , N>output ( intN ) ;
15 16 17 18 19 20 21
/ / computes th e r e s u l t ValueArraySink < i n t , N> s i n k = new ValueArraySink < i n t , N> ( ) ; var t = Tasks . s o u r c e ( intN ) => ( [ G e n e r i c P a t t e r n s . < i n t , R> r e c u r r e n c e ( t a s k T o o l k i t . i n c ) ] ) => t a s k s i n k . dump ; Timer t i m e r = new Timer ( ) ;
13
timer . s t a r t ( ) ; t . finish () ; t i m e r . s to p ( ) ;
22 23 24 25
/ / c o m p u t e s t h e r e f e r e n c e and c o m p a r e s i n t [N] r e f e r e n c e = new i n t [N] ( intN ) ; f o r ( i n t i ) { f o r ( i n t j ) { reference [ i ] = Toolkit . inc ( reference [ i ] ) ; } } s i n k . compareWith ( r e f e r e n c e ) ;
26 27 28 29 30 31 32 33
}
34 35 36
}
FT
The stencil pattern is a generalization of the map pattern where the elemental function can access not only a single element in an input collection but also a set of “neighbors” [McCool et al., 2012]. 1
/∗ s t e n c i l pattern ∗/
2
5 6
7 8 9 10 11 12 13
14 15
/∗ b a s ic version ∗ CALC : c a l c u l a t e s t h e s t r e a m o f new v a l u e s ∗ TI : s u p p o s e d t o b e ‘ (TO, TO, . . . , TO) ∗ / public t a s k s t a t i c l o c a l IsoTask s t e n c i l ( IsoTask CALC) { r e t u r n G e n e r i c P a t t e r n s . < TI , TO>map(CALC) ; }
RA
3 4
/∗ p a r a l l e l version ∗ CALC : c a l c u l a t e s t h e s t r e a m o f new v a l u e s ∗ TI : s u p p o s e d t o b e ‘ (TO, TO, . . . , TO) ∗ / public t a s k s t a t i c l o c a l IsoTask s t e n c i l P a r a l l e l ( IsoTask CALC) { r e t u r n G e n e r i c P a t t e r n s . < TI , TO, PP> m a p P a r a l l e l (CALC) ; } U SED IN: src/common/GenericPatterns.lime on page 1
D
The following test case shows how to use stencil pattern to calculate the sum of the two neighbors of each element in an integer value array:
1 2 3 4
package t e s t c a s e s . s t e n c i l ; import common . ∗ ; import lime . u t i l . Tasks ;
5 6
public c l a s s SumNeighbors {
7 8 9 10 11 12 13 14
/ ∗ c a l c u l a t e s t h e sum o f t h e two n e i g h b o r s o f e a c h e l e m e n t i n an i n t e g e r v a l u e a r r a y ∗ / public s t a t i c void main ( S t r i n g [ ] a r g s ) { System . out . p r i n t l n ( SumNeighbors . c l a s s . getName ( ) ) ; final int N = 8 , P = 4; i n t [ [N] ] intN = T o o l k i t . randomIntArray ( System . nanoTime ( ) ) ; System . out . p r i n t ( T o o l k i t . INPUT ) ; T o o l k i t . < i n t , N>output ( intN ) ;
14
15
/ / computes th e r e s u l t ValueArraySink < i n t , N> s i n k = new ValueArraySink < i n t , N> ( ) ; var t = Tasks . s o u r c e ( intN ) => ( # i n t [ [ 8 ] ] ) => t a s k T o o l k i t . genNeighbors => ( # ‘ ( i n t , i n t ) ) => ( [ G e n e r i c P a t t e r n s . < ‘ ( i n t , i n t ) , i n t , P> s t e n c i l P a r a l l e l ( t a s k T o o l k i t . add ) ] ) => t a s k s i n k . dump ; Timer t i m e r = new Timer ( ) ; timer . s t a r t ( ) ; t . finish () ; t i m e r . s to p ( ) ;
16 17 18 19 20 21 22 23 24 25 26 27 28
30 31 32 33 34 35 36
FT
/ / c o m p u t e s t h e r e f e r e n c e and c o m p a r e s i n t [N] r e f e r e n c e = new i n t [N] ( intN ) ; int s = 0; f o r ( i n t i ) { s = T o o l k i t . add ( ‘ ( intN [ ( i n t ) ( i − 1 ) ] , intN [ ( i n t ) ( i + 1 ) ] ) ) ; reference [ i ] = s ; } s i n k . compareWith ( r e f e r e n c e ) ;
29
}
37 38 39
}
RA
A reduction combines all the elements in a collection into a single element using an associative combiner function. Because the combiner function is associative, many orderings are possible [McCool et al., 2012].
2 3 4 5 6
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/∗ reduction pattern ∗/
/ ∗ F : r e d u c t i o n on PP e l e m e n t s ∗ NN: t h e s i z e o f t h e v a l u e a r r a y PP : t h e number o f sub −p r o b l e m s i n e a c h s t e p ∗ / public t a s k s t a t i c l o c a l IsoTask r e d u c t i o n ( IsoTask F ) { f i n a l i n t N = NN. s i z e ; f i n a l i n t P = PP . s i z e ; a s s e r t N >= P && T o o l k i t . isPowerOf (N, P ) ; f i n a l i n t SUB = N / P ; i f (N > P ) { IsoTask s u b r e d u c t i o n = G e n e r i c P a t t e r n s . r e d u c t i o n ( F ) ; r e t u r n G e n e r i c P a t t e r n s . f o r k j o i n ( s u b r e d u c t i o n ) // T => ( T # T [ [ PP ] ] ) / / T [ [ PP ] ] => F ; } else { / / a p p l y F when o n l y PP e l e m e n t s r e t u r n ( T # T [ [ PP ] ] ) => F ; } }
D
1
U SED IN: src/common/GenericPatterns.lime on page 1
Here is a test case of computing the product of an integer value array: 15
1
package t e s t c a s e s . r e d u c t i o n ;
2 3 4
import common . ∗ ; import lime . u t i l . Tasks ;
5 6
public c l a s s Product {
7
/ ∗ c a l c u l a t e s t h e p r o d u c t o f an i n t e g e r v a l u e a r r a y ∗ / public s t a t i c void main ( S t r i n g [ ] a r g s ) { System . out . p r i n t l n ( Product . c l a s s . getName ( ) ) ; final int N = 8; i n t [ [N] ] intN = T o o l k i t . randomIntArray ( System . nanoTime ( ) ) ; System . out . p r i n t ( T o o l k i t . INPUT ) ; T o o l k i t . < i n t , N>output ( intN ) ;
8 9 10 11 12 13 14 15
/ / computes th e r e s u l t ValueSink < i n t > s i n k = new ValueSink < i n t > ( ) ; var t = Tasks . s o u r c e ( intN ) => ( [ G e n e r i c P a t t e r n s . < i n t , N, 2> r e d u c t i o n ( t a s k T o o l k i t . mult ) ] ) => t a s k s i n k . dump ; Timer t i m e r = new Timer ( ) ; timer . s t a r t ( ) ; t . finish () ; t i m e r . s to p ( ) ;
FT
16 17 18 19 20 21 22 23 24 25
27 28 29 30 31 32 33 34
}
35 36 37
}
RA
/ / c o m p u t e s t h e r e f e r e n c e and c o m p a r e s int reference = 1; i n t [ 2 ] a = new i n t [ 2 ] ; f o r ( i n t i ) { a [0] = reference ; a [ 1 ] = intN [ i ] ; r e f e r e n c e = T o o l k i t . mult (new i n t [ [ 2 ] ] ( a ) ) ; } s i n k . compareWith ( r e f e r e n c e ) ;
26
1 2 3 4
5 6 7 8 9
10 11
D
Scan computes all partial reductions of a collection. In other words, for every output position, a reduction of the input up to that point is computed [McCool et al., 2012]. Note that here the basic operation must be on two elements, otherwise not every element has a partial reduction up to it.
/∗ scan p a t t e r n ∗/
/ ∗ TN : a g e n e r i c n u m e r i c t y p e w i t h + o p e r a t i o n , i s n e c e s s a r y f o r method s c a n _ m e r g e ∗ / public t a s k s t a t i c l o c a l IsoTask scan ( ) { f i n a l i n t N = NN. s i z e ; a s s e r t N >= 2 && T o o l k i t . isPowerOf (N, 2 ) ; i f (N > 2 ) { final int H = N / 2; r e t u r n G e n e r i c P a t t e r n s . f o r k j o i n O r d e r e d ( Permutations . p a r a l l e l i z e ( G e n e r i c P a t t e r n s . scan ( ) ) ) / / TN => Permutations . s e r i a l i z e ( t a s k G e n e r i c P a t t e r n s . scan_merge ) ;
16
} else { r e t u r n Permutations . s e r i a l i z e ( t a s k G e n e r i c P a t t e r n s . scan_merge ) ; }
12 13 14
15 16
}
17
19
20 21 22 23 24 25 26 27 28 29 30
/ ∗ m e r g e s t h e two s c a n n e d p a r t s ∗ / public s t a t i c l o c a l TN[ [NN] ] scan_merge (TN[ [NN ] ] in put ) { TN[NN] r e s u l t = new TN[NN] ( input ) ; f i n a l i n t N = NN. s i z e ; final int H = N / 2; / / t h e s e c o n d h a l f i n c r e a s e d by i [m − 1 ] / / c o u l d n o t f i n d a e q u i v a l e n t o p e r a t i o n on TI w i t h a f i l t e r a s p a r a m e t e r , / / s o TN was i n t r o d u c e d f o r (NN i = (NN) H; i > 0 ; i ++) { r e s u l t [ i ] += r e s u l t [ (NN) (H − 1 ) ] ; } r e t u r n new TN[ [NN] ] ( r e s u l t ) ; } U SED IN: src/common/GenericPatterns.lime on page 1
The following test case computes scan of summation:
FT
18
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
package t e s t c a s e s . scan ;
RA
2
import common . ∗ ; import lime . u t i l . Tasks ; public c l a s s Sum {
/ ∗ c a l c u l a t e s s c a n o f summation ∗ / public s t a t i c void main ( S t r i n g [ ] a r g s ) { System . out . p r i n t l n (Sum . c l a s s . getName ( ) ) ; final int N = 8; i n t [ [N] ] intN = T o o l k i t . randomIntArray ( System . nanoTime ( ) ) ; System . out . p r i n t ( T o o l k i t . INPUT ) ; T o o l k i t . < i n t , N>output ( intN ) ;
D
1
/ / computes th e r e s u l t ValueArraySink < i n t , N> s i n k = new ValueArraySink < i n t , N> ( ) ; var t = Tasks . s o u r c e ( intN ) => ( [ G e n e r i c P a t t e r n s . < i n t , 8> scan ( ) ] ) => t a s k s i n k . dump ; Timer t i m e r = new Timer ( ) ; timer . s t a r t ( ) ; t . finish () ; t i m e r . s to p ( ) ;
25 26 27 28 29 30 31 32
/ / c o m p u t e s t h e r e f e r e n c e and c o m p a r e s i n t [N] r e f e r e n c e = new i n t [N] ( intN ) ; int s = 0; f o r ( i n t i ) { s = T o o l k i t . add ( ‘ ( s , r e f e r e n c e [ i ] ) ) ; reference [ i ] = s ; }
17
s i n k . compareWith ( r e f e r e n c e ) ;
33
}
34 35 36
}
Divide and conquer differently pattern divides the problem into several sub-problems that potentially have different solutions, solve them in parallel, and merge the outputs into the final result. 1
/ ∗ d i v i d e and c o n q u e r d i f f e r e n t l y p a t t e r n ∗ /
2
5 6
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/ ∗ NN: t h e s i z e o f t h e v a l u e a r r a y ∗ SS : t h e s i z e o f sub −p r o b l e m s ∗ PP : t h e number o f sub −p r o b l e m s i n e a c h s t e p ∗ / public t a s k s t a t i c l o c a l IsoTask dncd ( IsoTask [ [ PP ] ] subs , IsoTask merge ) { f i n a l i n t N = NN. s i z e ; f i n a l i n t P = PP . s i z e ; final int S = N / P ; a s s e r t N == S ∗ P && P >= 2 && T o o l k i t . isPowerOf (N, P ) ; i f (N > P ) { r e t u r n ( T # T [ [ PP ] [ SS ] ] ) / / T [ [ PP ] [ SS ] ] => t a s k s p l i t T [ [ PP ] [ SS ] ] / / ‘ ( T [ SS ] , T [ SS ] , . . . , T [ SS ] ) => t a s k [ subs ] / / ‘ ( T [ SS ] , T [ SS ] , . . . , T [ SS ] ) => t a s k j o i n T [ [ PP ] [ SS ] ] / / T [ [ PP ] [ SS ] ] => ( T [ [ PP ] [ SS ] ] # T ) // T => merge ; } else { r e t u r n merge ; } }
FT
4
RA
3
28
31 32
/ ∗ by d e f a u l t PP = 2 ∗ / public t a s k s t a t i c l o c a l IsoTask dncdD ( IsoTask [ [ 2 ] ] subs , IsoTask merge ) { r e t u r n G e n e r i c P a t t e r n s . dncd ( subs , merge ) ; }
D
29 30
U SED IN: src/common/GenericPatterns.lime on page 1
An application would be bitonic sort (http://en.wikipedia.org/wiki/Bitonic_sorter), where the first half and the second half have different values of parameter upsort (part of the code is taken from Lime sample projects). 1
package examples . dncd ;
2 3 4
import common . ∗ ; import lime . u t i l . Tasks ;
5 6
public c l a s s B i t o n i c S o r t {
7 8
/∗ ∗
18
9 10 11 12 13 14 15 16 17
∗ Compares t h e two i n p u t k e y s and e x c h a n g e s t h e i r o r d e r i f t h e y a r e ∗ not s o r t e d . ∗ ∗ u p s o r t d e t e r m i n e s i f t h e s o r t i s n o n d e c r e a s i n g (UP) o r ∗ n o n i n c r e a s i n g (DOWN) . ’ t r u e ’ i n d i c a t e s UP s o r t and ’ f a l s e ’ ∗ i n d i c a t e s DOWN s o r t . ∗/ s t a t i c l o c a l ‘ ( i n t , i n t ) compareExchange ( ‘ ( i n t , i n t ) k , boolean u ps o r t ) { i n t mink , maxk ; i f ( k . 0 k1 ∗ / mink = k . 1 ; maxk = k . 0 ; }
19 20 21 22 23 24 25 26 27
i f ( u p so r t ) { r e t u r n ‘ ( mink , maxk ) ; / ∗ UP s o r t ∗ / } else { r e t u r n ‘ ( maxk , mink ) ; / ∗ DOWN s o r t ∗ / }
28 29 30 31 32 33 34
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
RA
37
}
/∗ ∗ ∗ P a r t i t i o n s t h e i n p u t b i t o n i c s e q u e n c e o f l e n g t h L i n t o two b i t o n i c ∗ s e q u e n c e s o f l e n g t h L / 2 , w i t h a l l numbers i n t h e f i r s t s e q u e n c e b i t o n i c _ c o m p a r e ( boolean u ps o r t ) { final int H = L . size / 2; var xchange = ( i n t # ‘ ( i n t , i n t ) ) => t a s k compareExchange ( ‘ ( i n t , i n t ) , u p so r t ) => ( # i n t ) ; / ∗ Each c o m p a r e E x c h a n g e e x a m i n e s k e y s t h a t a r e L / 2 e l e m e n t s a p a r t ∗ / r e t u r n ( i n t # i n t [ [H] ] ) => t a s k s p l i t i n t [ [H] ] => t a s k [ (H) xchange ] => t a s k j o i n i n t [ [H] ] => ( i n t [ [H] ] # i n t ) ; }
D
35 36
FT
18
60 61 62 63 64 65 66 67
s t a t i c l o c a l IsoTask < i n t , i n t > b i t o n i c _ l e v e l ( boolean u ps o r t ) { f i n a l i n t LL = L . s i z e ; final int H = L . size / 2; i f ( LL > 2 ) { r e t u r n B i t o n i c S o r t . b i t o n i c _ c o m p a r e ( u ps o rt ) => B i t o n i c S o r t . b i t o n i c _ l e v e l ( u ps o r t ) ; } else {
19
r e t u r n B i t o n i c S o r t . b i t o n i c _ c o m p a r e ( up s o rt ) ;
68
}
69
}
70 71
t a s k s t a t i c l o c a l IsoTask < i n t , i n t > c o n s t r u c t ( boolean u ps o r t ) { f i n a l i n t N = NN. s i z e ; i f (N > 2 ) { final int H = N / 2; IsoTask < i n t [ [H] ] , i n t [ [H] ] > [ 2 ] subs = new IsoTask < i n t [ [H] ] , i n t [ [H] ] > [ 2 ] ; subs [ 0 ] = Permutations . < i n t , i n t , H> p a r a l l e l i z e ( B i t o n i c S o r t . c o n s t r u c t ( u p s o r t ) ); subs [ 1 ] = Permutations . < i n t , i n t , H> p a r a l l e l i z e ( B i t o n i c S o r t . c o n s t r u c t ( ! u p s o r t )); IsoTask < i n t [ [H] ] , i n t [ [H] ] > [ [ 2 ] ] s u b t a s k s = new IsoTask < i n t [ [H] ] , i n t [ [H ] ] > [ [ 2 ] ] ( subs ) ; r e t u r n G e n e r i c P a t t e r n s . < i n t , NN, H, 2>dncd ( subtasks , B i t o n i c S o r t . b i t o n i c _ l e v e l ( u p so r t ) ) ; } else { r e t u r n B i t o n i c S o r t . b i t o n i c _ l e v e l ( up s o rt ) ; } }
72 73 74 75 76 77
78
79
FT
80
81 82 83 84 85
public s t a t i c void main ( S t r i n g [ ] a r g s ) { final int N = 8; i n t [ [N] ] intN = T o o l k i t . randomIntArray ( System . nanoTime ( ) ) ; System . out . p r i n t ( T o o l k i t . INPUT ) ; T o o l k i t . < i n t , N>output ( intN ) ;
86 87 88 89 90
RA
91
/ / computes th e r e s u l t ValueArraySink < i n t , N> s i n k = new ValueArraySink < i n t , N> ( ) ; var t = Tasks . s o u r c e ( intN ) => ( [ B i t o n i c S o r t . c o n s t r u c t ( t r u e ) ] ) => t a s k s i n k . dump ; Timer t i m e r = new Timer ( ) ; timer . s t a r t ( ) ; t . finish () ; t i m e r . s to p ( ) ;
92 93 94 95 96 97 98 99 100 101
/ / c o m p u t e s t h e r e f e r e n c e and c o m p a r e s i n t [N] r e f e r e n c e = new i n t [N] ( intN ) ; reference . sort ( ) ; s i n k . compareWith ( r e f e r e n c e ) ;
102 103
}
106 107 108
D
104 105
}
Divide and conquer pattern is the special case of divide and conquer differently pattern, where all the sub-solutions are the same. 1
/ ∗ d i v i d e and c o n q u e r p a t t e r n ∗ /
2 3 4 5 6
7 8
/ ∗ NN: t h e s i z e o f t h e v a l u e a r r a y ∗ SS : t h e s i z e o f sub −p r o b l e m s ∗ PP : t h e number o f sub −p r o b l e m s i n e a c h s t e p ∗ / public t a s k s t a t i c l o c a l IsoTask dnc ( IsoTask sub , IsoTask merge ) { f i n a l i n t N = NN. s i z e ; f i n a l i n t P = PP . s i z e ;
20
final int S = N / P ; a s s e r t N == S ∗ P && P >= 2 && T o o l k i t . isPowerOf (N, P ) ; IsoTask [ PP ] subs = new IsoTask [ PP ] ( ) ; f o r ( PP i ) { subs [ i ] = Permutations . p a r a l l e l i z e ( sub ) ; } IsoTask [ [ PP ] ] s u b t a s k s = new IsoTask [ [ PP ] ] ( subs ) ; i f (N > P ) { r e t u r n G e n e r i c P a t t e r n s . dncd ( subtasks , merge ) ; } else { r e t u r n merge ; }
9 10 11 12 13 14 15
16 17 18 19 20 21 22
}
23
26 27
/ ∗ by d e f a u l t PP = 2 ∗ / public t a s k s t a t i c l o c a l IsoTask dncD ( IsoTask sub , IsoTask merge ) { r e t u r n G e n e r i c P a t t e r n s . dnc ( sub , merge ) ; } U SED IN: src/common/GenericPatterns.lime on page 1
FT
24 25
One applicaiton would be merge sort (http://en.wikipedia.org/wiki/Merge_sort):
3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
package examples . dnc ;
RA
2
import common . ∗ ; import lime . u t i l . Tasks ;
public c l a s s MergeSort {
t a s k s t a t i c l o c a l IsoTask < i n t , i n t > c o n s t r u c t ( ) { f i n a l i n t N = NN. s i z e ; IsoTask < i n t , i n t > merge = Permutations . < i n t , i n t , NN> s e r i a l i z e ( t a s k MergeSort . merge ) ; i f (N > 2 ) { final int H = N / 2; r e t u r n G e n e r i c P a t t e r n s . < i n t , NN, H>dncD ( MergeSort . c o n s t r u c t ( ) , merge ) ; } else { r e t u r n merge ; } }
D
1
public s t a t i c l o c a l i n t [ [NN] ] merge ( i n t [ [NN] ] input ) { NN i = 0 ; NN H = (NN) (NN. s i z e / 2 ) ; NN j = H; NN c = 0 ; NN r = 0 ; f i n a l r e s u l t = new i n t [NN] ;
26 27 28 29 30 31
while ( t r u e ) { i n t l v = input [ i ] ; i n t uv = input [ j ] ; i f ( l v < uv ) { r e s u l t [ c ++] = l v ;
21
i ++; i f ( i == H) { r = j; break ; }
32 33 34 35 36
} else { r e s u l t [ c ++] = uv ; j ++; i f ( j == 0 ) { r = i; break ; / / NOTE: "1n" d o e s n ’ t work } }
37 38 39 40 41 42 43 44 45
}
46
48
//
NN r = i == 0 ? j : i ; while ( c ! = 0 ) { r e s u l t [ c ++] = input [ r + + ] ; }
49 50 51 52
r e t u r n new i n t [ [NN] ] ( r e s u l t ) ;
53
}
54 55
public s t a t i c void main ( S t r i n g [ ] a r g s ) { final int N = 8; i n t [ [N] ] intN = T o o l k i t . randomIntArray ( System . nanoTime ( ) ) ; System . out . p r i n t ( T o o l k i t . INPUT ) ; T o o l k i t . < i n t , N>output ( intN ) ;
56 57 58
RA
59 60 61
/ / computes th e r e s u l t ValueArraySink < i n t , N> s i n k = new ValueArraySink < i n t , N> ( ) ; var t = Tasks . s o u r c e ( intN ) => ( [ MergeSort . c o n s t r u c t ( ) ] ) => t a s k s i n k . dump ; Timer t i m e r = new Timer ( ) ; timer . s t a r t ( ) ; t . finish () ; t i m e r . s to p ( ) ;
62 63 64 65 66 67 68 69 70 71
/ / c o m p u t e s t h e r e f e r e n c e and c o m p a r e s i n t [N] r e f e r e n c e = new i n t [N] ( intN ) ; reference . sort ( ) ; s i n k . compareWith ( r e f e r e n c e ) ;
73 74 75
D
72
}
76 77 78
FT
47
}
Here is an example of fast Fourier transform (http://en.wikipedia.org/wiki/Fast_Fourier_transform): 1
package examples . dnc ;
2 3 4 5
import common . ∗ ; import lime . u t i l . PseudoRandom ; import lime . u t i l . Tasks ;
6 7
public c l a s s FFT {
8
22
9
public s t a t i c f i n a l long SEED = 1 0 1 0 1 0 1 0 ;
10 11 12 13
14 15 16 17 18 19 20
t a s k s t a t i c l o c a l IsoTask c o n s t r u c t ( ) { f i n a l i n t N = NN. s i z e ; IsoTask merge = Permutations . < complex , complex , NN> s e r i a l i z e ( t a s k FFT . merge ) ; i f (N > 2 ) { final int H = N / 2; r e t u r n G e n e r i c P a t t e r n s . < complex , NN, H>dncD ( FFT . c o n s t r u c t ( ) , merge ) ; } else { r e t u r n merge ; } }
21
23 24 25 26
public s t a t i c l o c a l complex [ [NN] ] merge ( complex [ [NN] ] input ) { f i n a l i n t N = NN. s i z e ; final int H = N / 2; f i n a l r e s u l t = new complex [NN] ; double t h e t a = − 2.0 f ∗ ( double ) Math . PI / ( ( double ) N) ;
FT
22
27
complex w = new complex ( 1 , 0 ) ; complex ww = new complex ( Math . cos ( t h e t a ) , Math . s i n ( t h e t a ) ) ;
28 29 30
32 33 34 35 36
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
54
}
public s t a t i c void s i n k ( complex c ) { System . out . p r i n t l n ( c ) ; }
public s t a t i c void main ( S t r i n g [ ] a r g s ) { final int N = 4; complex [ [N] ] complexN = T o o l k i t . randomComplexArray ( SEED ) ; System . out . p r i n t ( T o o l k i t . INPUT ) ; / / computes th e r e s u l t ValueArraySink s i n k = new ValueArraySink ( ) ; complex [ [N] ] complexI = Permutations . < complex , N> b i t _ r e v e r s e ( complexN ) ; T o o l k i t . < complex , N>output ( complexI ) ; var t = Tasks . s o u r c e ( complexN ) => Permutations . < complex , complex , N> s e r i a l i z e ( t a s k Permutations . < complex , N> bit_reverse ) => ( [ FFT . c o n s t r u c t ( ) ] )
D
37
RA
f o r (NN i = 0 ; i < H; i ++) { r e s u l t [ i ] = input [ i ] + w ∗ input [ (NN) ( i + H) ] ; r e s u l t [ (NN) ( i + H) ] = input [ i ] − w ∗ input [ (NN) ( i + H) ] ; w = w ∗ ww; } r e t u r n new complex [ [NN] ] ( r e s u l t ) ;
31
55 56 57 58 59 60
=> t a s k s i n k . dump ; Timer t i m e r = new Timer ( ) ; timer . s t a r t ( ) ; t . finish () ; t i m e r . s to p ( ) ;
61 62 63 64
/ / c o m p u t e s t h e r e f e r e n c e and c o m p a r e s complex [N] r e f e r e n c e = new complex [N] ( complexN ) , temp = new complex [N] ; r e f e r e n c e = new complex [N] ( Permutations . < complex , N> b i t _ r e v e r s e (new complex [ [N] ] ( reference ) ) ) ;
23
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
FT
f o r ( i n t d = 1 ; d < N; d ∗= 2 ) { double t h e t a = − 2.0 f ∗ ( double ) Math . PI / ( ( double ) d ∗ 2 ) ; complex w = new complex ( 1 , 0 ) ; complex ww = new complex ( Math . cos ( t h e t a ) , Math . s i n ( t h e t a ) ) ; int c = 0; complex temp1 , temp2 ; f o r ( i n t i = 0 ; i < N; i ++) { temp1 = r e f e r e n c e [ ( i n t ) i ] + w ∗ r e f e r e n c e [ ( i n t ) ( i + d ) ] ; temp2 = r e f e r e n c e [ ( i n t ) i ] − w ∗ r e f e r e n c e [ ( i n t ) ( i + d ) ] ; temp [ ( i n t ) i ] = temp1 ; temp [ ( i n t ) ( i + d ) ] = temp2 ; w = w ∗ ww; c ++; i f ( c == d ) { i += d ; w = new complex ( 1 , 0 ) ; c = 0; } } r e f e r e n c e = temp ; } s i n k . compareWith ( r e f e r e n c e ) ;
65
}
87 88
References
RA
}
Auerbach, J., D. F. Bacon, P. Cheng, and R. Rabbah (2010). Lime: The liquid metal programming language – language reference manual. Technical report, IBM Research Division. Gamma, E., R. Helm, R. Johnson, and J. M. Vlissides (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley Professional. Gosling, J., B. Joy, G. Steele, and G. Bracha (2005, May). The Java language specification, thrid edition. http: //docs.oracle.com/javase/specs/jls/se5.0/jls3.pdf. McCool, M., A. D. Robison, and J. Reinders (2012). Structured parallel programming: patterns for efficient computation. Amsterdam: Elsevier/Morgan Kaufmann.
D
89
24