繁体   English   中英

显示Java 8流处理的进度

[英]Show progress of Java 8 stream processing

我有一个Stream处理几百万个元素。 它背后的Map-Reduce算法需要几毫秒,因此任务完成大约需要20分钟。

Stream<MyData> myStream = readData();
MyResult result = myStream
    .map(row -> process(row))
    .peek(stat -> System.out.println("Hi, I processed another item"))
    .reduce(MyStat::aggregate);

我想要一种显示整体进度的方法,而不是每个元素打印一行(这导致每秒数千行,需要时间,并且不提供有关整体进度的任何有用信息)。 我想展示类似于:

 5% (08s)
10% (14s)
15% (20s)
...

最好(和/或最简单)的方法是什么?

首先,Streams并不是要实现这些任务(而不是传统的数据结构)。 如果你已经知道你的流将处理多少元素,你可以选择以下选项,我重复一遍,而不是流的目标。

Stream<MyData> myStream = readData();
final AtomicInteger loader = new AtomicInteger();
int fivePercent = elementsCount / 20;
MyResult result = myStream
    .map(row -> process(row))
    .peek(stat -> {
        if (loader.incrementAndGet() % fivePercent == 0) {
            System.out.println(loader.get() + " elements on " + elementsCount + " treated");
            System.out.println((5*(loader.get() / fivePercent)) + "%");
        }
    })
    .reduce(MyStat::aggregate);

正如其他人所指出的:这有一些警告。 首先,流不应该用于这样的事情。

在更技术层面,人们可以进一步争论:

  • 流可以是无限的
  • 即使您知道元素的数量:此数字可能会被filterflatMap等操作扭曲
  • 对于并行流,跟踪进度强制执行同步点
  • 如果存在昂贵的终端操作 (如您的情况下的聚合),则报告的进度可能甚至不能合理地反映计算时间

但是,记住这一点,对您的应用案例可能合理的一种方法是:

您可以创建一个传递给流mapFunction<T,T> (至少,我更喜欢在流上使用peek ,如另一个答案所示)。 此功能可以使用AtomicLong计算元素来跟踪进度。 为了将单独的事物分开,可以将此进度转发给Consumer<Long> ,它将负责演示

这里的“演示”是指将此进度打印到控制台,标准化或百分比,指的是在创建消费者的任何地方都可以知道的大小。 但是,消费者也可以仅处理打印,例如,每10个元素,或者如果自上一个元素以来已经过了至少5秒,则仅打印消息。

import java.util.Iterator;
import java.util.Locale;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.function.LongConsumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class StreamProgress
{
    public static void main(String[] args)
    {
        int size = 250;
        Stream<Integer> stream = readData(size);

        LongConsumer progressConsumer = progress -> 
        {
            // "Filter" the output here: Report only every 10th element
            if (progress % 10 == 0)
            {
                double relative = (double) progress / (size - 1);
                double percent = relative * 100;
                System.out.printf(Locale.ENGLISH,
                    "Progress %8d, relative %2.5f, percent %3.2f\n",
                    progress, relative, percent);
            }
        };

        Integer result = stream
            .map(element -> process(element))
            .map(progressMapper(progressConsumer))
            .reduce(0, (a, b) -> a + b);

        System.out.println("result " + result);
    }

    private static <T> Function<T, T> progressMapper(
        LongConsumer progressConsumer)
    {
        AtomicLong counter = new AtomicLong(0);
        return t -> 
        {
            long n = counter.getAndIncrement();
            progressConsumer.accept(n);
            return t;
        };

    }

    private static Integer process(Integer element)
    {
        return element * 2;
    }

    private static Stream<Integer> readData(int size)
    {
        Iterator<Integer> iterator = new Iterator<Integer>()
        {
            int n = 0;
            @Override
            public Integer next()
            {
                try
                {
                    Thread.sleep(10);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                return n++;
            }

            @Override
            public boolean hasNext()
            {
                return n < size;
            }
        };
        return StreamSupport.stream(
            Spliterators.spliteratorUnknownSize(
                iterator, Spliterator.ORDERED), false);
    }
}

这样做的可能性很大程度上取决于您在stream中使用的source类型。 如果你有一个集合,并且你想对它应用一些操作,你可以这样做,因为你知道集合的大小,你可以保留已处理元素的数量。 但在这种情况下也有一个警告。 如果您将在流中进行并行计算,那么这也变得更加困难。

在您从应用程序外部传输数据的情况下,您很难对流程进行建模,因为您不知道流何时结束。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM