ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/ArrayPrefixHelpers.java
Revision: 1.7
Committed: Sun Sep 20 15:33:12 2015 UTC (8 years, 7 months ago) by jsr166
Branch: MAIN
Changes since 1.6: +8 -4 lines
Log Message:
make nested classes private where possible

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