-
Notifications
You must be signed in to change notification settings - Fork 413
/
syncsingle.chpl
213 lines (180 loc) · 6.71 KB
/
syncsingle.chpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// Sync / Singles
//
// This primer illustrates Chapel's sync and single variables.
//
// ``Sync`` and ``single`` are type qualifiers that can be applied to all
// Chapel primitive types except ``strings``, ``complex``, and ``record``.
//
// Sync and single variables have an associated state that is either
// ``full`` or ``empty``. Single variables are sync variables that can
// only be written once. If a sync or single variable is declared
// with an initializing expression, its state is set to ``full`` and its
// value is that of the expression. Without an initializing
// expression, a sync or single variable's state is set to ``empty`` and
// its value is the default value for the base type.
//
config const n = 7;
var sy$: sync int=1; // state = full, value = 1
var si$: single bool; // state = empty, value = false
// By convention, sync and single variable names end in a ``$`` to
// add a visual cue indicating that reads and writes are possibly
// expensive operations and can potentially block the task.
//
// For both sync and single variables, the state must be ``empty`` before
// the value can be written. When the write has completed, the state
// is set to ``full``.
//
si$ = true; // state = full, value = true
// For both sync and single variables, the state must be ``full`` before the
// value can be read. When the read has completed, the state of a
// sync variable is set to ``empty``, and the state of a single variable
// remains ``full``.
//
var sy = sy$; // sy$: state = empty
var si = si$; // si$: state = full
// If a sync or single variable is not in the correct state for reading
// (``full``) or writing (``empty``), the current task blocks.
//
// The following creates a new task via a :ref:`begin
// <primers-taskparallel-begin>` statement and declares a variable ``sy``
// that is initialized to ``sy$``. The initialization statement will block
// until ``sy$`` is ``full``. The last statement in the ``begin`` block sets
// ``done$`` to ``full``.
//
var done$: sync bool;
writeln("Launching new task");
begin {
var sy = sy$; // This statement will block until sy$ is full
writeln("New task unblocked, sy=", sy);
done$ = true;
}
// Recall that execution proceeds immediately following the ``begin``
// statement after task creation.
//
writeln("After launching new task");
// When ``sy$`` is written, its state will be set to ``full`` and the blocked
// task above will continue.
//
sy$ = n;
// This next statement blocks until the last statement in the above ``begin``
// completes.
//
done$;
//
// Example: simple split-phase barrier for tasks
//
var count$: sync int = n; // counter which also serves as a lock
var release$: single bool; // barrier release
coforall t in 1..n {
var myc = count$; // read the count, grab the lock (state = empty)
if myc!=1 { // still waiting for others
write(".");
count$ = myc-1; // update the count, release the lock (state = full)
// we could do some work while waiting
release$; // wait for everyone
} else { // last one here
release$ = true; // release everyone first (state = full)
writeln("done");
}
}
//
// There are a number of methods defined for sync and single variables.
//
// The ``reset()`` method, defined for sync variables, sets the value of
// the variable to the default value for the type and the state to
// ``empty``.
//
sy$.reset(); // value = 0, state = empty
// The ``isFull`` method returns ``true`` if the sync or single variable is
// in the ``full`` state, ``false`` otherwise.
//
writeln(sy$.isFull);
writeln(si$.isFull);
// The ``writeEF()`` method, defined for sync and single variables,
// blocks until the state is ``empty`` and then assigns the value argument
// to the variable and then sets the state to ``full``. Assignment of
// sync and single variables are performed using ``writeEF()``.
//
sy$.writeEF(2*n); // equivalent to: sy$ = 2*n;
// The ``readFE()`` method, defined for sync variables, blocks until the
// state is ``full`` and then reads the value of the variable, sets the
// state to ``empty``, and then returns the value. Normal reads of sync
// variables are performed using ``readFE()``.
//
var sy2 = sy$.readFE(); // equivalent to: var sy2 = sy$;
writeln(sy2);
// The ``readFF()`` method, defined for sync and single variables, blocks
// until the state is ``full`` and then reads the value of the variable
// and returns the value. The state remains ``full``. Normal reads of
// single variables are performed using ``readFF()``.
//
var si2 = si$.readFF();
writeln(si2);
// The ``writeXF()`` method, defined for sync variables, assigns the
// value argument to the variable and then sets the state to ``full``.
// This method does not block.
//
sy$.writeXF(3*n);
// The ``readXX()`` method, defined for sync and single variables, returns
// the value of the variable regardless of the state. This method
// does not block and the state is unchanged.
//
var sy3 = sy$.readXX();
var si3 = si$.readXX();
writeln(sy3);
writeln(si3);
// The ``writeFF()`` method, defined for sync variables, blocks until the
// state is ``full`` and then and then assigns the value argument to the
// variable. The state is unchanged.
//
sy$.writeFF(4*n);
sy$.reset();
// Sync and single arguments are passed by reference. As a result,
// the state of the variable does not change.
//
writeln(sy$.isFull);
f_withSyncIntFormal(sy$);
writeln(si$.isFull);
f_withSingleBoolFormal(si$);
sy$ = 4*n;
// When a sync or single variable is passed as an argument to a
// function that expects the base type of the variable, the value is
// read before being passed to the function. Therefore, the task will
// block until the state of the variable is ``full``.
//
f_withIntFormal(sy$);
f_withBoolFormal(si$);
// When passing a sync or single variable to a generic formal,
// whether with a ``ref`` intent or a default intent, the variable
// is passed by reference. The state of the variable does not
// change and sync operations are available.
//
f_withGenericDefaultIntentFormal(sy$);
f_withGenericDefaultIntentFormal(si$);
f_withGenericRefFormal(sy$);
f_withGenericRefFormal(si$);
sy$ = 5*n;
// Currently, sync and single variables cannot be written out directly.
// We need to extract the value, for example using ``readFE()`` or ``readFF()``.
//
writeln(sy$.readFE());
writeln(si$.readFF());
// Definitions of functions used above
proc f_withSyncIntFormal(x: sync int) {
writeln(x.isFull);
}
proc f_withSingleBoolFormal(x: single bool) {
writeln(x.isFull);
}
proc f_withIntFormal(x: int) {
writeln(x);
}
proc f_withBoolFormal(x: bool) {
writeln(x);
}
proc f_withGenericDefaultIntentFormal(x) {
writeln("the full bit is: ", x.isFull);
}
proc f_withGenericRefFormal(ref x) {
writeln("readXX returns: ", x.readXX());
}