ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/ArrayPrefixHelpers.java
Revision: 1.5
Committed: Sat Sep 19 18:36:55 2015 UTC (8 years, 7 months ago) by jsr166
Branch: MAIN
Changes since 1.4: +4 -60 lines
Log Message:
delete unused "Root task constructor"s

File Contents

# Content
1 /*
2 * Written by Doug Lea with assistance from members of JCP JSR-166
3 * Expert Group and released to the public domain, as explained at
4 * http://creativecommons.org/publicdomain/zero/1.0/
5 */
6
7 package java.util;
8
9 import java.util.concurrent.CountedCompleter;
10 import java.util.concurrent.ForkJoinPool;
11 import java.util.function.BinaryOperator;
12 import java.util.function.DoubleBinaryOperator;
13 import java.util.function.IntBinaryOperator;
14 import java.util.function.LongBinaryOperator;
15
16 /**
17 * ForkJoin tasks to perform Arrays.parallelPrefix operations.
18 *
19 * @author Doug Lea
20 * @since 1.8
21 */
22 class ArrayPrefixHelpers {
23 private ArrayPrefixHelpers() {} // non-instantiable
24
25 /*
26 * Parallel prefix (aka cumulate, scan) task classes
27 * are based loosely on Guy Blelloch's original
28 * algorithm (http://www.cs.cmu.edu/~scandal/alg/scan.html):
29 * Keep dividing by two to threshold segment size, and then:
30 * Pass 1: Create tree of partial sums for each segment
31 * Pass 2: For each segment, cumulate with offset of left sibling
32 *
33 * This version improves performance within FJ framework mainly by
34 * allowing the second pass of ready left-hand sides to proceed
35 * even if some right-hand side first passes are still executing.
36 * It also combines first and second pass for leftmost segment,
37 * and skips the first pass for rightmost segment (whose result is
38 * not needed for second pass). It similarly manages to avoid
39 * requiring that users supply an identity basis for accumulations
40 * by tracking those segments/subtasks for which the first
41 * existing element is used as base.
42 *
43 * Managing this relies on ORing some bits in the pendingCount for
44 * phases/states: CUMULATE, SUMMED, and FINISHED. CUMULATE is the
45 * main phase bit. When false, segments compute only their sum.
46 * When true, they cumulate array elements. CUMULATE is set at
47 * root at beginning of second pass and then propagated down. But
48 * it may also be set earlier for subtrees with lo==0 (the left
49 * spine of tree). SUMMED is a one bit join count. For leafs, it
50 * is set when summed. For internal nodes, it becomes true when
51 * one child is summed. When the second child finishes summing,
52 * we then moves up tree to trigger the cumulate phase. FINISHED
53 * is also a one bit join count. For leafs, it is set when
54 * cumulated. For internal nodes, it becomes true when one child
55 * is cumulated. When the second child finishes cumulating, it
56 * then moves up tree, completing at the root.
57 *
58 * To better exploit locality and reduce overhead, the compute
59 * method loops starting with the current task, moving if possible
60 * to one of its subtasks rather than forking.
61 *
62 * As usual for this sort of utility, there are 4 versions, that
63 * are simple copy/paste/adapt variants of each other. (The
64 * double and int versions differ from long version solely by
65 * replacing "long" (with case-matching)).
66 */
67
68 // see above
69 static final int CUMULATE = 1;
70 static final int SUMMED = 2;
71 static final int FINISHED = 4;
72
73 /** The smallest subtask array partition size to use as threshold */
74 static final int MIN_PARTITION = 16;
75
76 static final class CumulateTask<T> extends CountedCompleter<Void> {
77 final T[] array;
78 final BinaryOperator<T> function;
79 CumulateTask<T> left, right;
80 T in, out;
81 final int lo, hi, origin, fence, threshold;
82
83 CumulateTask(CumulateTask<T> parent, BinaryOperator<T> function,
84 T[] array, int origin, int fence, int threshold,
85 int lo, int hi) {
86 super(parent);
87 this.function = function; this.array = array;
88 this.origin = origin; this.fence = fence;
89 this.threshold = threshold;
90 this.lo = lo; this.hi = hi;
91 }
92
93 public final void compute() {
94 final BinaryOperator<T> fn;
95 final T[] a;
96 if ((fn = this.function) == null || (a = this.array) == null)
97 throw new NullPointerException(); // hoist checks
98 int th = threshold, org = origin, fnc = fence, l, h;
99 CumulateTask<T> t = this;
100 outer: while ((l = t.lo) >= 0 && (h = t.hi) <= a.length) {
101 if (h - l > th) {
102 CumulateTask<T> lt = t.left, rt = t.right, f;
103 if (lt == null) { // first pass
104 int mid = (l + h) >>> 1;
105 f = rt = t.right =
106 new CumulateTask<T>(t, fn, a, org, fnc, th, mid, h);
107 t = lt = t.left =
108 new CumulateTask<T>(t, fn, a, org, fnc, th, l, mid);
109 }
110 else { // possibly refork
111 T pin = t.in;
112 lt.in = pin;
113 f = t = null;
114 if (rt != null) {
115 T lout = lt.out;
116 rt.in = (l == org ? lout :
117 fn.apply(pin, lout));
118 for (int c;;) {
119 if (((c = rt.getPendingCount()) & CUMULATE) != 0)
120 break;
121 if (rt.compareAndSetPendingCount(c, c|CUMULATE)){
122 t = rt;
123 break;
124 }
125 }
126 }
127 for (int c;;) {
128 if (((c = lt.getPendingCount()) & CUMULATE) != 0)
129 break;
130 if (lt.compareAndSetPendingCount(c, c|CUMULATE)) {
131 if (t != null)
132 f = t;
133 t = lt;
134 break;
135 }
136 }
137 if (t == null)
138 break;
139 }
140 if (f != null)
141 f.fork();
142 }
143 else {
144 int state; // Transition to sum, cumulate, or both
145 for (int b;;) {
146 if (((b = t.getPendingCount()) & FINISHED) != 0)
147 break outer; // already done
148 state = ((b & CUMULATE) != 0 ? FINISHED :
149 (l > org) ? SUMMED : (SUMMED|FINISHED));
150 if (t.compareAndSetPendingCount(b, b|state))
151 break;
152 }
153
154 T sum;
155 if (state != SUMMED) {
156 int first;
157 if (l == org) { // leftmost; no in
158 sum = a[org];
159 first = org + 1;
160 }
161 else {
162 sum = t.in;
163 first = l;
164 }
165 for (int i = first; i < h; ++i) // cumulate
166 a[i] = sum = fn.apply(sum, a[i]);
167 }
168 else if (h < fnc) { // skip rightmost
169 sum = a[l];
170 for (int i = l + 1; i < h; ++i) // sum only
171 sum = fn.apply(sum, a[i]);
172 }
173 else
174 sum = t.in;
175 t.out = sum;
176 for (CumulateTask<T> par;;) { // propagate
177 @SuppressWarnings("unchecked") CumulateTask<T> partmp
178 = (CumulateTask<T>)t.getCompleter();
179 if ((par = partmp) == null) {
180 if ((state & FINISHED) != 0) // enable join
181 t.quietlyComplete();
182 break outer;
183 }
184 int b = par.getPendingCount();
185 if ((b & state & FINISHED) != 0)
186 t = par; // both done
187 else if ((b & state & SUMMED) != 0) { // both summed
188 int nextState; CumulateTask<T> lt, rt;
189 if ((lt = par.left) != null &&
190 (rt = par.right) != null) {
191 T lout = lt.out;
192 par.out = (rt.hi == fnc ? lout :
193 fn.apply(lout, rt.out));
194 }
195 int refork = (((b & CUMULATE) == 0 &&
196 par.lo == org) ? CUMULATE : 0);
197 if ((nextState = b|state|refork) == b ||
198 par.compareAndSetPendingCount(b, nextState)) {
199 state = SUMMED; // drop finished
200 t = par;
201 if (refork != 0)
202 par.fork();
203 }
204 }
205 else if (par.compareAndSetPendingCount(b, b|state))
206 break outer; // sib not ready
207 }
208 }
209 }
210 }
211 private static final long serialVersionUID = 5293554502939613543L;
212 }
213
214 static final class LongCumulateTask extends CountedCompleter<Void> {
215 final long[] array;
216 final LongBinaryOperator function;
217 LongCumulateTask left, right;
218 long in, out;
219 final int lo, hi, origin, fence, threshold;
220
221 LongCumulateTask(LongCumulateTask parent, LongBinaryOperator function,
222 long[] array, int origin, int fence, int threshold,
223 int lo, int hi) {
224 super(parent);
225 this.function = function; this.array = array;
226 this.origin = origin; this.fence = fence;
227 this.threshold = threshold;
228 this.lo = lo; this.hi = hi;
229 }
230
231 public final void compute() {
232 final LongBinaryOperator fn;
233 final long[] a;
234 if ((fn = this.function) == null || (a = this.array) == null)
235 throw new NullPointerException(); // hoist checks
236 int th = threshold, org = origin, fnc = fence, l, h;
237 LongCumulateTask t = this;
238 outer: while ((l = t.lo) >= 0 && (h = t.hi) <= a.length) {
239 if (h - l > th) {
240 LongCumulateTask lt = t.left, rt = t.right, f;
241 if (lt == null) { // first pass
242 int mid = (l + h) >>> 1;
243 f = rt = t.right =
244 new LongCumulateTask(t, fn, a, org, fnc, th, mid, h);
245 t = lt = t.left =
246 new LongCumulateTask(t, fn, a, org, fnc, th, l, mid);
247 }
248 else { // possibly refork
249 long pin = t.in;
250 lt.in = pin;
251 f = t = null;
252 if (rt != null) {
253 long lout = lt.out;
254 rt.in = (l == org ? lout :
255 fn.applyAsLong(pin, lout));
256 for (int c;;) {
257 if (((c = rt.getPendingCount()) & CUMULATE) != 0)
258 break;
259 if (rt.compareAndSetPendingCount(c, c|CUMULATE)){
260 t = rt;
261 break;
262 }
263 }
264 }
265 for (int c;;) {
266 if (((c = lt.getPendingCount()) & CUMULATE) != 0)
267 break;
268 if (lt.compareAndSetPendingCount(c, c|CUMULATE)) {
269 if (t != null)
270 f = t;
271 t = lt;
272 break;
273 }
274 }
275 if (t == null)
276 break;
277 }
278 if (f != null)
279 f.fork();
280 }
281 else {
282 int state; // Transition to sum, cumulate, or both
283 for (int b;;) {
284 if (((b = t.getPendingCount()) & FINISHED) != 0)
285 break outer; // already done
286 state = ((b & CUMULATE) != 0 ? FINISHED :
287 (l > org) ? SUMMED : (SUMMED|FINISHED));
288 if (t.compareAndSetPendingCount(b, b|state))
289 break;
290 }
291
292 long sum;
293 if (state != SUMMED) {
294 int first;
295 if (l == org) { // leftmost; no in
296 sum = a[org];
297 first = org + 1;
298 }
299 else {
300 sum = t.in;
301 first = l;
302 }
303 for (int i = first; i < h; ++i) // cumulate
304 a[i] = sum = fn.applyAsLong(sum, a[i]);
305 }
306 else if (h < fnc) { // skip rightmost
307 sum = a[l];
308 for (int i = l + 1; i < h; ++i) // sum only
309 sum = fn.applyAsLong(sum, a[i]);
310 }
311 else
312 sum = t.in;
313 t.out = sum;
314 for (LongCumulateTask par;;) { // propagate
315 if ((par = (LongCumulateTask)t.getCompleter()) == null) {
316 if ((state & FINISHED) != 0) // enable join
317 t.quietlyComplete();
318 break outer;
319 }
320 int b = par.getPendingCount();
321 if ((b & state & FINISHED) != 0)
322 t = par; // both done
323 else if ((b & state & SUMMED) != 0) { // both summed
324 int nextState; LongCumulateTask lt, rt;
325 if ((lt = par.left) != null &&
326 (rt = par.right) != null) {
327 long lout = lt.out;
328 par.out = (rt.hi == fnc ? lout :
329 fn.applyAsLong(lout, rt.out));
330 }
331 int refork = (((b & CUMULATE) == 0 &&
332 par.lo == org) ? CUMULATE : 0);
333 if ((nextState = b|state|refork) == b ||
334 par.compareAndSetPendingCount(b, nextState)) {
335 state = SUMMED; // drop finished
336 t = par;
337 if (refork != 0)
338 par.fork();
339 }
340 }
341 else if (par.compareAndSetPendingCount(b, b|state))
342 break outer; // sib not ready
343 }
344 }
345 }
346 }
347 private static final long serialVersionUID = -5074099945909284273L;
348 }
349
350 static final class DoubleCumulateTask extends CountedCompleter<Void> {
351 final double[] array;
352 final DoubleBinaryOperator function;
353 DoubleCumulateTask left, right;
354 double in, out;
355 final int lo, hi, origin, fence, threshold;
356
357 DoubleCumulateTask(DoubleCumulateTask parent, DoubleBinaryOperator function,
358 double[] array, int origin, int fence, int threshold,
359 int lo, int hi) {
360 super(parent);
361 this.function = function; this.array = array;
362 this.origin = origin; this.fence = fence;
363 this.threshold = threshold;
364 this.lo = lo; this.hi = hi;
365 }
366
367 public final void compute() {
368 final DoubleBinaryOperator fn;
369 final double[] a;
370 if ((fn = this.function) == null || (a = this.array) == null)
371 throw new NullPointerException(); // hoist checks
372 int th = threshold, org = origin, fnc = fence, l, h;
373 DoubleCumulateTask t = this;
374 outer: while ((l = t.lo) >= 0 && (h = t.hi) <= a.length) {
375 if (h - l > th) {
376 DoubleCumulateTask lt = t.left, rt = t.right, f;
377 if (lt == null) { // first pass
378 int mid = (l + h) >>> 1;
379 f = rt = t.right =
380 new DoubleCumulateTask(t, fn, a, org, fnc, th, mid, h);
381 t = lt = t.left =
382 new DoubleCumulateTask(t, fn, a, org, fnc, th, l, mid);
383 }
384 else { // possibly refork
385 double pin = t.in;
386 lt.in = pin;
387 f = t = null;
388 if (rt != null) {
389 double lout = lt.out;
390 rt.in = (l == org ? lout :
391 fn.applyAsDouble(pin, lout));
392 for (int c;;) {
393 if (((c = rt.getPendingCount()) & CUMULATE) != 0)
394 break;
395 if (rt.compareAndSetPendingCount(c, c|CUMULATE)){
396 t = rt;
397 break;
398 }
399 }
400 }
401 for (int c;;) {
402 if (((c = lt.getPendingCount()) & CUMULATE) != 0)
403 break;
404 if (lt.compareAndSetPendingCount(c, c|CUMULATE)) {
405 if (t != null)
406 f = t;
407 t = lt;
408 break;
409 }
410 }
411 if (t == null)
412 break;
413 }
414 if (f != null)
415 f.fork();
416 }
417 else {
418 int state; // Transition to sum, cumulate, or both
419 for (int b;;) {
420 if (((b = t.getPendingCount()) & FINISHED) != 0)
421 break outer; // already done
422 state = ((b & CUMULATE) != 0 ? FINISHED :
423 (l > org) ? SUMMED : (SUMMED|FINISHED));
424 if (t.compareAndSetPendingCount(b, b|state))
425 break;
426 }
427
428 double sum;
429 if (state != SUMMED) {
430 int first;
431 if (l == org) { // leftmost; no in
432 sum = a[org];
433 first = org + 1;
434 }
435 else {
436 sum = t.in;
437 first = l;
438 }
439 for (int i = first; i < h; ++i) // cumulate
440 a[i] = sum = fn.applyAsDouble(sum, a[i]);
441 }
442 else if (h < fnc) { // skip rightmost
443 sum = a[l];
444 for (int i = l + 1; i < h; ++i) // sum only
445 sum = fn.applyAsDouble(sum, a[i]);
446 }
447 else
448 sum = t.in;
449 t.out = sum;
450 for (DoubleCumulateTask par;;) { // propagate
451 if ((par = (DoubleCumulateTask)t.getCompleter()) == null) {
452 if ((state & FINISHED) != 0) // enable join
453 t.quietlyComplete();
454 break outer;
455 }
456 int b = par.getPendingCount();
457 if ((b & state & FINISHED) != 0)
458 t = par; // both done
459 else if ((b & state & SUMMED) != 0) { // both summed
460 int nextState; DoubleCumulateTask lt, rt;
461 if ((lt = par.left) != null &&
462 (rt = par.right) != null) {
463 double lout = lt.out;
464 par.out = (rt.hi == fnc ? lout :
465 fn.applyAsDouble(lout, rt.out));
466 }
467 int refork = (((b & CUMULATE) == 0 &&
468 par.lo == org) ? CUMULATE : 0);
469 if ((nextState = b|state|refork) == b ||
470 par.compareAndSetPendingCount(b, nextState)) {
471 state = SUMMED; // drop finished
472 t = par;
473 if (refork != 0)
474 par.fork();
475 }
476 }
477 else if (par.compareAndSetPendingCount(b, b|state))
478 break outer; // sib not ready
479 }
480 }
481 }
482 }
483 private static final long serialVersionUID = -586947823794232033L;
484 }
485
486 static final class IntCumulateTask extends CountedCompleter<Void> {
487 final int[] array;
488 final IntBinaryOperator function;
489 IntCumulateTask left, right;
490 int in, out;
491 final int lo, hi, origin, fence, threshold;
492
493 IntCumulateTask(IntCumulateTask parent, IntBinaryOperator function,
494 int[] array, int origin, int fence, int threshold,
495 int lo, int hi) {
496 super(parent);
497 this.function = function; this.array = array;
498 this.origin = origin; this.fence = fence;
499 this.threshold = threshold;
500 this.lo = lo; this.hi = hi;
501 }
502
503 public final void compute() {
504 final IntBinaryOperator fn;
505 final int[] a;
506 if ((fn = this.function) == null || (a = this.array) == null)
507 throw new NullPointerException(); // hoist checks
508 int th = threshold, org = origin, fnc = fence, l, h;
509 IntCumulateTask t = this;
510 outer: while ((l = t.lo) >= 0 && (h = t.hi) <= a.length) {
511 if (h - l > th) {
512 IntCumulateTask lt = t.left, rt = t.right, f;
513 if (lt == null) { // first pass
514 int mid = (l + h) >>> 1;
515 f = rt = t.right =
516 new IntCumulateTask(t, fn, a, org, fnc, th, mid, h);
517 t = lt = t.left =
518 new IntCumulateTask(t, fn, a, org, fnc, th, l, mid);
519 }
520 else { // possibly refork
521 int pin = t.in;
522 lt.in = pin;
523 f = t = null;
524 if (rt != null) {
525 int lout = lt.out;
526 rt.in = (l == org ? lout :
527 fn.applyAsInt(pin, lout));
528 for (int c;;) {
529 if (((c = rt.getPendingCount()) & CUMULATE) != 0)
530 break;
531 if (rt.compareAndSetPendingCount(c, c|CUMULATE)){
532 t = rt;
533 break;
534 }
535 }
536 }
537 for (int c;;) {
538 if (((c = lt.getPendingCount()) & CUMULATE) != 0)
539 break;
540 if (lt.compareAndSetPendingCount(c, c|CUMULATE)) {
541 if (t != null)
542 f = t;
543 t = lt;
544 break;
545 }
546 }
547 if (t == null)
548 break;
549 }
550 if (f != null)
551 f.fork();
552 }
553 else {
554 int state; // Transition to sum, cumulate, or both
555 for (int b;;) {
556 if (((b = t.getPendingCount()) & FINISHED) != 0)
557 break outer; // already done
558 state = ((b & CUMULATE) != 0 ? FINISHED :
559 (l > org) ? SUMMED : (SUMMED|FINISHED));
560 if (t.compareAndSetPendingCount(b, b|state))
561 break;
562 }
563
564 int sum;
565 if (state != SUMMED) {
566 int first;
567 if (l == org) { // leftmost; no in
568 sum = a[org];
569 first = org + 1;
570 }
571 else {
572 sum = t.in;
573 first = l;
574 }
575 for (int i = first; i < h; ++i) // cumulate
576 a[i] = sum = fn.applyAsInt(sum, a[i]);
577 }
578 else if (h < fnc) { // skip rightmost
579 sum = a[l];
580 for (int i = l + 1; i < h; ++i) // sum only
581 sum = fn.applyAsInt(sum, a[i]);
582 }
583 else
584 sum = t.in;
585 t.out = sum;
586 for (IntCumulateTask par;;) { // propagate
587 if ((par = (IntCumulateTask)t.getCompleter()) == null) {
588 if ((state & FINISHED) != 0) // enable join
589 t.quietlyComplete();
590 break outer;
591 }
592 int b = par.getPendingCount();
593 if ((b & state & FINISHED) != 0)
594 t = par; // both done
595 else if ((b & state & SUMMED) != 0) { // both summed
596 int nextState; IntCumulateTask lt, rt;
597 if ((lt = par.left) != null &&
598 (rt = par.right) != null) {
599 int lout = lt.out;
600 par.out = (rt.hi == fnc ? lout :
601 fn.applyAsInt(lout, rt.out));
602 }
603 int refork = (((b & CUMULATE) == 0 &&
604 par.lo == org) ? CUMULATE : 0);
605 if ((nextState = b|state|refork) == b ||
606 par.compareAndSetPendingCount(b, nextState)) {
607 state = SUMMED; // drop finished
608 t = par;
609 if (refork != 0)
610 par.fork();
611 }
612 }
613 else if (par.compareAndSetPendingCount(b, b|state))
614 break outer; // sib not ready
615 }
616 }
617 }
618 }
619 private static final long serialVersionUID = 3731755594596840961L;
620 }
621
622 }