ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/CountedCompleter.java
(Generate patch)

Comparing jsr166/src/main/java/util/concurrent/CountedCompleter.java (file contents):
Revision 1.57 by dl, Fri Jun 17 13:03:45 2016 UTC vs.
Revision 1.58 by jsr166, Mon Aug 29 19:13:16 2016 UTC

# Line 91 | Line 91 | import java.lang.invoke.VarHandle;
91   * to complete for some elements than others, either because of
92   * intrinsic variation (for example I/O) or auxiliary effects such as
93   * garbage collection.  Because CountedCompleters provide their own
94 < * continuations, other threads need not block waiting to perform
95 < * them.
94 > * continuations, other tasks need not block waiting to perform them.
95   *
96 < * <p>For example, here is an initial version of a class that uses
97 < * divide-by-two recursive decomposition to divide work into single
98 < * pieces (leaf tasks). Even when work is split into individual calls,
99 < * tree-based techniques are usually preferable to directly forking
100 < * leaf tasks, because they reduce inter-thread communication and
101 < * improve load balancing. In the recursive case, the second of each
102 < * pair of subtasks to finish triggers completion of its parent
96 > * <p>For example, here is an initial version of a utility method that
97 > * uses divide-by-two recursive decomposition to divide work into
98 > * single pieces (leaf tasks). Even when work is split into individual
99 > * calls, tree-based techniques are usually preferable to directly
100 > * forking leaf tasks, because they reduce inter-thread communication
101 > * and improve load balancing. In the recursive case, the second of
102 > * each pair of subtasks to finish triggers completion of their parent
103   * (because no result combination is performed, the default no-op
104   * implementation of method {@code onCompletion} is not overridden).
105 < * A static utility method sets up the base task and invokes it
106 < * (here, implicitly using the {@link ForkJoinPool#commonPool()}).
105 > * The utility method sets up the root task and invokes it (here,
106 > * implicitly using the {@link ForkJoinPool#commonPool()}).  It is
107 > * straightforward and reliable (but not optimal) to always set the
108 > * pending count to the number of child tasks and call {@code
109 > * tryComplete()} immediately before returning.
110   *
111   * <pre> {@code
112 < * class MyOperation<E> { void apply(E e) { ... }  }
113 < *
114 < * class ForEach<E> extends CountedCompleter<Void> {
115 < *
116 < *   public static <E> void forEach(E[] array, MyOperation<E> op) {
117 < *     new ForEach<E>(null, array, op, 0, array.length).invoke();
118 < *   }
119 < *
120 < *   final E[] array; final MyOperation<E> op; final int lo, hi;
121 < *   ForEach(CountedCompleter<?> p, E[] array, MyOperation<E> op, int lo, int hi) {
122 < *     super(p);
123 < *     this.array = array; this.op = op; this.lo = lo; this.hi = hi;
124 < *   }
125 < *
126 < *   public void compute() { // version 1
127 < *     if (hi - lo >= 2) {
128 < *       int mid = (lo + hi) >>> 1;
129 < *       setPendingCount(2); // must set pending count before fork
130 < *       new ForEach(this, array, op, mid, hi).fork(); // right child
129 < *       new ForEach(this, array, op, lo, mid).fork(); // left child
130 < *     }
131 < *     else if (hi > lo)
132 < *       op.apply(array[lo]);
133 < *     tryComplete();
112 > * public static <E> void forEach(E[] array, Consumer<E> action) {
113 > *   class Task extends CountedCompleter<Void> {
114 > *     final int lo, hi;
115 > *     Task(Task parent, int lo, int hi) {
116 > *       super(parent); this.lo = lo; this.hi = hi;
117 > *     }
118 > *
119 > *     public void compute() {
120 > *       if (hi - lo >= 2) {
121 > *         int mid = (lo + hi) >>> 1;
122 > *         // must set pending count before fork
123 > *         setPendingCount(2);
124 > *         new Task(this, mid, hi).fork(); // right child
125 > *         new Task(this, lo, mid).fork(); // left child
126 > *       }
127 > *       else if (hi > lo)
128 > *         action.accept(array[lo]);
129 > *       tryComplete();
130 > *     }
131   *   }
132 + *   new Task(null, 0, array.length).invoke();
133   * }}</pre>
134   *
135   * This design can be improved by noticing that in the recursive case,
136   * the task has nothing to do after forking its right task, so can
137   * directly invoke its left task before returning. (This is an analog
138 < * of tail recursion removal.)  Also, because the task returns upon
139 < * executing its left task (rather than falling through to invoke
140 < * {@code tryComplete}) the pending count is set to one:
138 > * of tail recursion removal.)  Also, when the last action in a task
139 > * is to fork or invoke a subtask (a "tail call"), the call to {@code
140 > * tryComplete()} can be optimized away, at the cost of making the
141 > * pending count look "off by one".
142   *
143   * <pre> {@code
144 < * class ForEach<E> ... {
145 < *   ...
146 < *   public void compute() { // version 2
147 < *     if (hi - lo >= 2) {
148 < *       int mid = (lo + hi) >>> 1;
149 < *       setPendingCount(1); // only one pending
150 < *       new ForEach(this, array, op, mid, hi).fork(); // right child
151 < *       new ForEach(this, array, op, lo, mid).compute(); // direct invoke
152 < *     }
153 < *     else {
154 < *       if (hi > lo)
156 < *         op.apply(array[lo]);
157 < *       tryComplete();
144 > *     public void compute() {
145 > *       if (hi - lo >= 2) {
146 > *         int mid = (lo + hi) >>> 1;
147 > *         setPendingCount(1); // not off by one !
148 > *         new Task(this, mid, hi).fork(); // right child
149 > *         new Task(this, lo, mid).compute(); // direct invoke
150 > *       } else {
151 > *         if (hi > lo)
152 > *           action.accept(array[lo]);
153 > *         tryComplete();
154 > *       }
155   *     }
156 < *   }
160 < * }}</pre>
156 > *   }}</pre>
157   *
158   * As a further optimization, notice that the left task need not even exist.
159   * Instead of creating a new one, we can iterate using the original task,
160   * and add a pending count for each fork.  Additionally, because no task
161   * in this tree implements an {@link #onCompletion(CountedCompleter)} method,
162 < * {@code tryComplete()} can be replaced with {@link #propagateCompletion}.
162 > * {@code tryComplete} can be replaced with {@link #propagateCompletion}.
163   *
164   * <pre> {@code
165 < * class ForEach<E> ... {
166 < *   ...
167 < *   public void compute() { // version 3
168 < *     int l = lo, h = hi;
169 < *     while (h - l >= 2) {
170 < *       int mid = (l + h) >>> 1;
171 < *       addToPendingCount(1);
172 < *       new ForEach(this, array, op, mid, h).fork(); // right child
173 < *       h = mid;
174 < *     }
175 < *     if (h > l)
176 < *       op.apply(array[l]);
177 < *     propagateCompletion();
165 > *     public void compute() {
166 > *       int n = hi - lo;
167 > *       for (; n >= 2; n /= 2) {
168 > *         addToPendingCount(1);
169 > *         new Task(this, lo + n/2, lo + n).fork();
170 > *       }
171 > *       if (n > 0)
172 > *         action.accept(array[lo]);
173 > *       propagateCompletion();
174 > *     }
175 > *   }}</pre>
176 > *
177 > * When pending counts can be precomputed, they can be established in
178 > * the constructor:
179 > *
180 > * <pre> {@code
181 > * public static <E> void forEach(E[] array, Consumer<E> action) {
182 > *   class Task extends CountedCompleter<Void> {
183 > *     final int lo, hi;
184 > *     Task(Task parent, int lo, int hi) {
185 > *       super(parent, 31 - Integer.numberOfLeadingZeros(hi - lo));
186 > *       this.lo = lo; this.hi = hi;
187 > *     }
188 > *
189 > *     public void compute() {
190 > *       for (int n = hi - lo; n >= 2; n /= 2)
191 > *         new Task(this, lo + n/2, lo + n).fork();
192 > *       action.accept(array[lo]);
193 > *       propagateCompletion();
194 > *     }
195   *   }
196 + *   if (array.length > 0)
197 + *     new Task(null, 0, array.length).invoke();
198   * }}</pre>
199   *
200 < * Additional optimizations of such classes might entail precomputing
201 < * pending counts so that they can be established in constructors,
202 < * specializing classes for leaf steps, subdividing by say, four,
203 < * instead of two per iteration, and using an adaptive threshold
189 < * instead of always subdividing down to single elements.
200 > * Additional optimizations of such classes might entail specializing
201 > * classes for leaf steps, subdividing by say, four, instead of two
202 > * per iteration, and using an adaptive threshold instead of always
203 > * subdividing down to single elements.
204   *
205   * <p><b>Searching.</b> A tree of CountedCompleters can search for a
206   * value or property in different parts of a data structure, and

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines