Regression (b92): OOM on filter/limit operation on unbound/parallel stream
Paul Sandoz
paul.sandoz at oracle.com
Wed Jun 5 02:03:22 PDT 2013
On Jun 5, 2013, at 8:14 AM, Peter Levart <peter.levart at gmail.com> wrote:
> On 06/04/2013 09:03 PM, Paul Sandoz wrote:
>> Digging more into the problem... of course i made the fatal/silly mistake of prematurely blaming hotspot/gc :-)
>>
>> In addition to the catching of the OOME the other problem is the main task creation loop is too aggressive creating tasks for a spliterator from iterator. Tasks are created faster than they can be processed and this gets worse as the numbers get larger since it takes more time to check if a number is a prime number. Normally this is ok as spliterators are light-weight, but the spliterator from iterator copies stuff into an array of increasing size.
>
> But how do you explain that you have no problems when running in interpreted mode?
I can induce a problem in interpretive mode if i reduce the maximum memory. It basically causes the OOME which then causes the spin loop.
> Is the ratio of execution speed of "task creation" : "task execution" lower in interpreted mode than in compiled mode?
>
That would be by guess. Intuitively i would have expected things to balance out but i am guessing GCs result in larger pauses to the main thread (is that possible?) and perhaps the loop to calculate primes is proportionally much faster in interpretive mode than the creation of tasks.
I have a working hack in AbstractTask so i can get the max memory down to 32m:
public final void compute() {
@SuppressWarnings("unchecked")
K task = (K) this;
while (task.canCompute()) {
if (task.spliterator.estimateSize() == Long.MAX_VALUE) {
ForkJoinPool p = ForkJoinPool.commonPool();
while (p.hasQueuedSubmissions() && p.getQueuedSubmissionCount() > 8) {
p.awaitQuiescence(0, TimeUnit.MILLISECONDS);
}
}
Spliterator<P_IN> split;
if (!task.suggestSplit() || (split = task.spliterator.trySplit()) == null) {
task.setLocalResult(task.doLeaf());
task.tryComplete();
return;
}
else {
K l = task.leftChild = task.makeChild(split);
K r = task.rightChild = task.makeChild(task.spliterator);
task.spliterator = null;
task.setPendingCount(1);
l.fork();
task = r;
}
}
}
For a spliterator from iterator of infinite size this basically enables the root task, on the main thread, to help out if there are pending tasks. I think in general this is probably useful for any large estimate e.g. >= 1 >> 24 to protect against spliterator from iterator since we cannot accurately detect them (although i have pondered a spliterator characteristic e.g. RIGHTBALANCED)
Paul.
More information about the lambda-dev
mailing list