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