ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/ArrayPrefixHelpers.java
Revision: 1.11
Committed: Fri Aug 30 18:05:39 2019 UTC (4 years, 8 months ago) by jsr166
Branch: MAIN
Changes since 1.10: +4 -0 lines
Log Message:
accommodate 8229997: Apply java.io.Serial annotations in java.base

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 /** Root task constructor */
84 public CumulateTask(CumulateTask<T> parent,
85 BinaryOperator<T> function,
86 T[] array, int lo, int hi) {
87 super(parent);
88 this.function = function; this.array = array;
89 this.lo = this.origin = lo; this.hi = this.fence = hi;
90 int p;
91 this.threshold =
92 (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3))
93 <= MIN_PARTITION ? MIN_PARTITION : p;
94 }
95
96 /** Subtask constructor */
97 CumulateTask(CumulateTask<T> parent, BinaryOperator<T> function,
98 T[] array, int origin, int fence, int threshold,
99 int lo, int hi) {
100 super(parent);
101 this.function = function; this.array = array;
102 this.origin = origin; this.fence = fence;
103 this.threshold = threshold;
104 this.lo = lo; this.hi = hi;
105 }
106
107 public final void compute() {
108 final BinaryOperator<T> fn;
109 final T[] a;
110 if ((fn = this.function) == null || (a = this.array) == null)
111 throw new NullPointerException(); // hoist checks
112 int th = threshold, org = origin, fnc = fence, l, h;
113 CumulateTask<T> t = this;
114 outer: while ((l = t.lo) >= 0 && (h = t.hi) <= a.length) {
115 if (h - l > th) {
116 CumulateTask<T> lt = t.left, rt = t.right, f;
117 if (lt == null) { // first pass
118 int mid = (l + h) >>> 1;
119 f = rt = t.right =
120 new CumulateTask<T>(t, fn, a, org, fnc, th, mid, h);
121 t = lt = t.left =
122 new CumulateTask<T>(t, fn, a, org, fnc, th, l, mid);
123 }
124 else { // possibly refork
125 T pin = t.in;
126 lt.in = pin;
127 f = t = null;
128 if (rt != null) {
129 T lout = lt.out;
130 rt.in = (l == org ? lout :
131 fn.apply(pin, lout));
132 for (int c;;) {
133 if (((c = rt.getPendingCount()) & CUMULATE) != 0)
134 break;
135 if (rt.compareAndSetPendingCount(c, c|CUMULATE)){
136 t = rt;
137 break;
138 }
139 }
140 }
141 for (int c;;) {
142 if (((c = lt.getPendingCount()) & CUMULATE) != 0)
143 break;
144 if (lt.compareAndSetPendingCount(c, c|CUMULATE)) {
145 if (t != null)
146 f = t;
147 t = lt;
148 break;
149 }
150 }
151 if (t == null)
152 break;
153 }
154 if (f != null)
155 f.fork();
156 }
157 else {
158 int state; // Transition to sum, cumulate, or both
159 for (int b;;) {
160 if (((b = t.getPendingCount()) & FINISHED) != 0)
161 break outer; // already done
162 state = ((b & CUMULATE) != 0 ? FINISHED :
163 (l > org) ? SUMMED : (SUMMED|FINISHED));
164 if (t.compareAndSetPendingCount(b, b|state))
165 break;
166 }
167
168 T sum;
169 if (state != SUMMED) {
170 int first;
171 if (l == org) { // leftmost; no in
172 sum = a[org];
173 first = org + 1;
174 }
175 else {
176 sum = t.in;
177 first = l;
178 }
179 for (int i = first; i < h; ++i) // cumulate
180 a[i] = sum = fn.apply(sum, a[i]);
181 }
182 else if (h < fnc) { // skip rightmost
183 sum = a[l];
184 for (int i = l + 1; i < h; ++i) // sum only
185 sum = fn.apply(sum, a[i]);
186 }
187 else
188 sum = t.in;
189 t.out = sum;
190 for (CumulateTask<T> par;;) { // propagate
191 @SuppressWarnings("unchecked") CumulateTask<T> partmp
192 = (CumulateTask<T>)t.getCompleter();
193 if ((par = partmp) == null) {
194 if ((state & FINISHED) != 0) // enable join
195 t.quietlyComplete();
196 break outer;
197 }
198 int b = par.getPendingCount();
199 if ((b & state & FINISHED) != 0)
200 t = par; // both done
201 else if ((b & state & SUMMED) != 0) { // both summed
202 int nextState; CumulateTask<T> lt, rt;
203 if ((lt = par.left) != null &&
204 (rt = par.right) != null) {
205 T lout = lt.out;
206 par.out = (rt.hi == fnc ? lout :
207 fn.apply(lout, rt.out));
208 }
209 int refork = (((b & CUMULATE) == 0 &&
210 par.lo == org) ? CUMULATE : 0);
211 if ((nextState = b|state|refork) == b ||
212 par.compareAndSetPendingCount(b, nextState)) {
213 state = SUMMED; // drop finished
214 t = par;
215 if (refork != 0)
216 par.fork();
217 }
218 }
219 else if (par.compareAndSetPendingCount(b, b|state))
220 break outer; // sib not ready
221 }
222 }
223 }
224 }
225 // OPENJDK @java.io.Serial
226 private static final long serialVersionUID = 5293554502939613543L;
227 }
228
229 static final class LongCumulateTask extends CountedCompleter<Void> {
230 final long[] array;
231 final LongBinaryOperator function;
232 LongCumulateTask left, right;
233 long in, out;
234 final int lo, hi, origin, fence, threshold;
235
236 /** Root task constructor */
237 public LongCumulateTask(LongCumulateTask parent,
238 LongBinaryOperator function,
239 long[] array, int lo, int hi) {
240 super(parent);
241 this.function = function; this.array = array;
242 this.lo = this.origin = lo; this.hi = this.fence = hi;
243 int p;
244 this.threshold =
245 (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3))
246 <= MIN_PARTITION ? MIN_PARTITION : p;
247 }
248
249 /** Subtask constructor */
250 LongCumulateTask(LongCumulateTask parent, LongBinaryOperator function,
251 long[] array, int origin, int fence, int threshold,
252 int lo, int hi) {
253 super(parent);
254 this.function = function; this.array = array;
255 this.origin = origin; this.fence = fence;
256 this.threshold = threshold;
257 this.lo = lo; this.hi = hi;
258 }
259
260 public final void compute() {
261 final LongBinaryOperator fn;
262 final long[] a;
263 if ((fn = this.function) == null || (a = this.array) == null)
264 throw new NullPointerException(); // hoist checks
265 int th = threshold, org = origin, fnc = fence, l, h;
266 LongCumulateTask t = this;
267 outer: while ((l = t.lo) >= 0 && (h = t.hi) <= a.length) {
268 if (h - l > th) {
269 LongCumulateTask lt = t.left, rt = t.right, f;
270 if (lt == null) { // first pass
271 int mid = (l + h) >>> 1;
272 f = rt = t.right =
273 new LongCumulateTask(t, fn, a, org, fnc, th, mid, h);
274 t = lt = t.left =
275 new LongCumulateTask(t, fn, a, org, fnc, th, l, mid);
276 }
277 else { // possibly refork
278 long pin = t.in;
279 lt.in = pin;
280 f = t = null;
281 if (rt != null) {
282 long lout = lt.out;
283 rt.in = (l == org ? lout :
284 fn.applyAsLong(pin, lout));
285 for (int c;;) {
286 if (((c = rt.getPendingCount()) & CUMULATE) != 0)
287 break;
288 if (rt.compareAndSetPendingCount(c, c|CUMULATE)){
289 t = rt;
290 break;
291 }
292 }
293 }
294 for (int c;;) {
295 if (((c = lt.getPendingCount()) & CUMULATE) != 0)
296 break;
297 if (lt.compareAndSetPendingCount(c, c|CUMULATE)) {
298 if (t != null)
299 f = t;
300 t = lt;
301 break;
302 }
303 }
304 if (t == null)
305 break;
306 }
307 if (f != null)
308 f.fork();
309 }
310 else {
311 int state; // Transition to sum, cumulate, or both
312 for (int b;;) {
313 if (((b = t.getPendingCount()) & FINISHED) != 0)
314 break outer; // already done
315 state = ((b & CUMULATE) != 0 ? FINISHED :
316 (l > org) ? SUMMED : (SUMMED|FINISHED));
317 if (t.compareAndSetPendingCount(b, b|state))
318 break;
319 }
320
321 long sum;
322 if (state != SUMMED) {
323 int first;
324 if (l == org) { // leftmost; no in
325 sum = a[org];
326 first = org + 1;
327 }
328 else {
329 sum = t.in;
330 first = l;
331 }
332 for (int i = first; i < h; ++i) // cumulate
333 a[i] = sum = fn.applyAsLong(sum, a[i]);
334 }
335 else if (h < fnc) { // skip rightmost
336 sum = a[l];
337 for (int i = l + 1; i < h; ++i) // sum only
338 sum = fn.applyAsLong(sum, a[i]);
339 }
340 else
341 sum = t.in;
342 t.out = sum;
343 for (LongCumulateTask par;;) { // propagate
344 if ((par = (LongCumulateTask)t.getCompleter()) == null) {
345 if ((state & FINISHED) != 0) // enable join
346 t.quietlyComplete();
347 break outer;
348 }
349 int b = par.getPendingCount();
350 if ((b & state & FINISHED) != 0)
351 t = par; // both done
352 else if ((b & state & SUMMED) != 0) { // both summed
353 int nextState; LongCumulateTask lt, rt;
354 if ((lt = par.left) != null &&
355 (rt = par.right) != null) {
356 long lout = lt.out;
357 par.out = (rt.hi == fnc ? lout :
358 fn.applyAsLong(lout, rt.out));
359 }
360 int refork = (((b & CUMULATE) == 0 &&
361 par.lo == org) ? CUMULATE : 0);
362 if ((nextState = b|state|refork) == b ||
363 par.compareAndSetPendingCount(b, nextState)) {
364 state = SUMMED; // drop finished
365 t = par;
366 if (refork != 0)
367 par.fork();
368 }
369 }
370 else if (par.compareAndSetPendingCount(b, b|state))
371 break outer; // sib not ready
372 }
373 }
374 }
375 }
376 // OPENJDK @java.io.Serial
377 private static final long serialVersionUID = -5074099945909284273L;
378 }
379
380 static final class DoubleCumulateTask extends CountedCompleter<Void> {
381 final double[] array;
382 final DoubleBinaryOperator function;
383 DoubleCumulateTask left, right;
384 double in, out;
385 final int lo, hi, origin, fence, threshold;
386
387 /** Root task constructor */
388 public DoubleCumulateTask(DoubleCumulateTask parent,
389 DoubleBinaryOperator function,
390 double[] array, int lo, int hi) {
391 super(parent);
392 this.function = function; this.array = array;
393 this.lo = this.origin = lo; this.hi = this.fence = hi;
394 int p;
395 this.threshold =
396 (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3))
397 <= MIN_PARTITION ? MIN_PARTITION : p;
398 }
399
400 /** Subtask constructor */
401 DoubleCumulateTask(DoubleCumulateTask parent, DoubleBinaryOperator function,
402 double[] array, int origin, int fence, int threshold,
403 int lo, int hi) {
404 super(parent);
405 this.function = function; this.array = array;
406 this.origin = origin; this.fence = fence;
407 this.threshold = threshold;
408 this.lo = lo; this.hi = hi;
409 }
410
411 public final void compute() {
412 final DoubleBinaryOperator fn;
413 final double[] a;
414 if ((fn = this.function) == null || (a = this.array) == null)
415 throw new NullPointerException(); // hoist checks
416 int th = threshold, org = origin, fnc = fence, l, h;
417 DoubleCumulateTask t = this;
418 outer: while ((l = t.lo) >= 0 && (h = t.hi) <= a.length) {
419 if (h - l > th) {
420 DoubleCumulateTask lt = t.left, rt = t.right, f;
421 if (lt == null) { // first pass
422 int mid = (l + h) >>> 1;
423 f = rt = t.right =
424 new DoubleCumulateTask(t, fn, a, org, fnc, th, mid, h);
425 t = lt = t.left =
426 new DoubleCumulateTask(t, fn, a, org, fnc, th, l, mid);
427 }
428 else { // possibly refork
429 double pin = t.in;
430 lt.in = pin;
431 f = t = null;
432 if (rt != null) {
433 double lout = lt.out;
434 rt.in = (l == org ? lout :
435 fn.applyAsDouble(pin, lout));
436 for (int c;;) {
437 if (((c = rt.getPendingCount()) & CUMULATE) != 0)
438 break;
439 if (rt.compareAndSetPendingCount(c, c|CUMULATE)){
440 t = rt;
441 break;
442 }
443 }
444 }
445 for (int c;;) {
446 if (((c = lt.getPendingCount()) & CUMULATE) != 0)
447 break;
448 if (lt.compareAndSetPendingCount(c, c|CUMULATE)) {
449 if (t != null)
450 f = t;
451 t = lt;
452 break;
453 }
454 }
455 if (t == null)
456 break;
457 }
458 if (f != null)
459 f.fork();
460 }
461 else {
462 int state; // Transition to sum, cumulate, or both
463 for (int b;;) {
464 if (((b = t.getPendingCount()) & FINISHED) != 0)
465 break outer; // already done
466 state = ((b & CUMULATE) != 0 ? FINISHED :
467 (l > org) ? SUMMED : (SUMMED|FINISHED));
468 if (t.compareAndSetPendingCount(b, b|state))
469 break;
470 }
471
472 double sum;
473 if (state != SUMMED) {
474 int first;
475 if (l == org) { // leftmost; no in
476 sum = a[org];
477 first = org + 1;
478 }
479 else {
480 sum = t.in;
481 first = l;
482 }
483 for (int i = first; i < h; ++i) // cumulate
484 a[i] = sum = fn.applyAsDouble(sum, a[i]);
485 }
486 else if (h < fnc) { // skip rightmost
487 sum = a[l];
488 for (int i = l + 1; i < h; ++i) // sum only
489 sum = fn.applyAsDouble(sum, a[i]);
490 }
491 else
492 sum = t.in;
493 t.out = sum;
494 for (DoubleCumulateTask par;;) { // propagate
495 if ((par = (DoubleCumulateTask)t.getCompleter()) == null) {
496 if ((state & FINISHED) != 0) // enable join
497 t.quietlyComplete();
498 break outer;
499 }
500 int b = par.getPendingCount();
501 if ((b & state & FINISHED) != 0)
502 t = par; // both done
503 else if ((b & state & SUMMED) != 0) { // both summed
504 int nextState; DoubleCumulateTask lt, rt;
505 if ((lt = par.left) != null &&
506 (rt = par.right) != null) {
507 double lout = lt.out;
508 par.out = (rt.hi == fnc ? lout :
509 fn.applyAsDouble(lout, rt.out));
510 }
511 int refork = (((b & CUMULATE) == 0 &&
512 par.lo == org) ? CUMULATE : 0);
513 if ((nextState = b|state|refork) == b ||
514 par.compareAndSetPendingCount(b, nextState)) {
515 state = SUMMED; // drop finished
516 t = par;
517 if (refork != 0)
518 par.fork();
519 }
520 }
521 else if (par.compareAndSetPendingCount(b, b|state))
522 break outer; // sib not ready
523 }
524 }
525 }
526 }
527 // OPENJDK @java.io.Serial
528 private static final long serialVersionUID = -586947823794232033L;
529 }
530
531 static final class IntCumulateTask extends CountedCompleter<Void> {
532 final int[] array;
533 final IntBinaryOperator function;
534 IntCumulateTask left, right;
535 int in, out;
536 final int lo, hi, origin, fence, threshold;
537
538 /** Root task constructor */
539 public IntCumulateTask(IntCumulateTask parent,
540 IntBinaryOperator function,
541 int[] array, int lo, int hi) {
542 super(parent);
543 this.function = function; this.array = array;
544 this.lo = this.origin = lo; this.hi = this.fence = hi;
545 int p;
546 this.threshold =
547 (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3))
548 <= MIN_PARTITION ? MIN_PARTITION : p;
549 }
550
551 /** Subtask constructor */
552 IntCumulateTask(IntCumulateTask parent, IntBinaryOperator function,
553 int[] array, int origin, int fence, int threshold,
554 int lo, int hi) {
555 super(parent);
556 this.function = function; this.array = array;
557 this.origin = origin; this.fence = fence;
558 this.threshold = threshold;
559 this.lo = lo; this.hi = hi;
560 }
561
562 public final void compute() {
563 final IntBinaryOperator fn;
564 final int[] a;
565 if ((fn = this.function) == null || (a = this.array) == null)
566 throw new NullPointerException(); // hoist checks
567 int th = threshold, org = origin, fnc = fence, l, h;
568 IntCumulateTask t = this;
569 outer: while ((l = t.lo) >= 0 && (h = t.hi) <= a.length) {
570 if (h - l > th) {
571 IntCumulateTask lt = t.left, rt = t.right, f;
572 if (lt == null) { // first pass
573 int mid = (l + h) >>> 1;
574 f = rt = t.right =
575 new IntCumulateTask(t, fn, a, org, fnc, th, mid, h);
576 t = lt = t.left =
577 new IntCumulateTask(t, fn, a, org, fnc, th, l, mid);
578 }
579 else { // possibly refork
580 int pin = t.in;
581 lt.in = pin;
582 f = t = null;
583 if (rt != null) {
584 int lout = lt.out;
585 rt.in = (l == org ? lout :
586 fn.applyAsInt(pin, lout));
587 for (int c;;) {
588 if (((c = rt.getPendingCount()) & CUMULATE) != 0)
589 break;
590 if (rt.compareAndSetPendingCount(c, c|CUMULATE)){
591 t = rt;
592 break;
593 }
594 }
595 }
596 for (int c;;) {
597 if (((c = lt.getPendingCount()) & CUMULATE) != 0)
598 break;
599 if (lt.compareAndSetPendingCount(c, c|CUMULATE)) {
600 if (t != null)
601 f = t;
602 t = lt;
603 break;
604 }
605 }
606 if (t == null)
607 break;
608 }
609 if (f != null)
610 f.fork();
611 }
612 else {
613 int state; // Transition to sum, cumulate, or both
614 for (int b;;) {
615 if (((b = t.getPendingCount()) & FINISHED) != 0)
616 break outer; // already done
617 state = ((b & CUMULATE) != 0 ? FINISHED :
618 (l > org) ? SUMMED : (SUMMED|FINISHED));
619 if (t.compareAndSetPendingCount(b, b|state))
620 break;
621 }
622
623 int sum;
624 if (state != SUMMED) {
625 int first;
626 if (l == org) { // leftmost; no in
627 sum = a[org];
628 first = org + 1;
629 }
630 else {
631 sum = t.in;
632 first = l;
633 }
634 for (int i = first; i < h; ++i) // cumulate
635 a[i] = sum = fn.applyAsInt(sum, a[i]);
636 }
637 else if (h < fnc) { // skip rightmost
638 sum = a[l];
639 for (int i = l + 1; i < h; ++i) // sum only
640 sum = fn.applyAsInt(sum, a[i]);
641 }
642 else
643 sum = t.in;
644 t.out = sum;
645 for (IntCumulateTask par;;) { // propagate
646 if ((par = (IntCumulateTask)t.getCompleter()) == null) {
647 if ((state & FINISHED) != 0) // enable join
648 t.quietlyComplete();
649 break outer;
650 }
651 int b = par.getPendingCount();
652 if ((b & state & FINISHED) != 0)
653 t = par; // both done
654 else if ((b & state & SUMMED) != 0) { // both summed
655 int nextState; IntCumulateTask lt, rt;
656 if ((lt = par.left) != null &&
657 (rt = par.right) != null) {
658 int lout = lt.out;
659 par.out = (rt.hi == fnc ? lout :
660 fn.applyAsInt(lout, rt.out));
661 }
662 int refork = (((b & CUMULATE) == 0 &&
663 par.lo == org) ? CUMULATE : 0);
664 if ((nextState = b|state|refork) == b ||
665 par.compareAndSetPendingCount(b, nextState)) {
666 state = SUMMED; // drop finished
667 t = par;
668 if (refork != 0)
669 par.fork();
670 }
671 }
672 else if (par.compareAndSetPendingCount(b, b|state))
673 break outer; // sib not ready
674 }
675 }
676 }
677 }
678 // OPENJDK @java.io.Serial
679 private static final long serialVersionUID = 3731755594596840961L;
680 }
681 }