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

# User Rev Content
1 dl 1.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 jsr166 1.4
9     import java.util.concurrent.CountedCompleter;
10 dl 1.1 import java.util.function.BinaryOperator;
11 jsr166 1.4 import java.util.function.DoubleBinaryOperator;
12 dl 1.1 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 jsr166 1.3 private ArrayPrefixHelpers() {} // non-instantiable
23 dl 1.1
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 jsr166 1.7 private static final class CumulateTask<T>
76     extends CountedCompleter<Void> {
77 dl 1.1 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 jsr166 1.2 @SuppressWarnings("unchecked") CumulateTask<T> partmp
178     = (CumulateTask<T>)t.getCompleter();
179     if ((par = partmp) == null) {
180 dl 1.1 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 jsr166 1.7 private static final class LongCumulateTask
215     extends CountedCompleter<Void> {
216 dl 1.1 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 jsr166 1.7 private static final class DoubleCumulateTask
352     extends CountedCompleter<Void> {
353 dl 1.1 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 jsr166 1.5 double[] array, int origin, int fence, int threshold,
361     int lo, int hi) {
362 dl 1.1 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 jsr166 1.7 private static final class IntCumulateTask
489     extends CountedCompleter<Void> {
490 dl 1.1 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 jsr166 1.5 int[] array, int origin, int fence, int threshold,
498     int lo, int hi) {
499 dl 1.1 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     }