ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/ArrayPrefixHelpers.java
Revision: 1.12
Committed: Thu Oct 10 16:53:08 2019 UTC (4 years, 6 months ago) by jsr166
Branch: MAIN
Changes since 1.11: +8 -1 lines
Log Message:
8231202: Suppress warnings on non-serializable non-transient instance fields in serializable classes

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