如何将计时器度量添加到java.util.Stream

如何将计时器度量添加到java.util.Stream,java,Java,一般关注的是如何向java.util.Stream执行的各个部分添加计时度量。在终止时,很容易对整个操作进行计时,例如(使用codahale库) 但“每件商品”的时间安排呢?或者如何向流的中间部分添加计时器,例如,计时10级流的前5级需要多长时间 只需在这些方法中添加计时器,就可以轻松地为中间阶段的各个步骤计时。初始拆分器代码可以测量tryAdvance第一次出现与close()方法之间的时间(它必须向生成的流中添加一个onClose Runnable)。这至少允许流提供库使用计时器,即使它们不

一般关注的是如何向java.util.Stream执行的各个部分添加计时度量。在终止时,很容易对整个操作进行计时,例如(使用codahale库)

但“每件商品”的时间安排呢?或者如何向流的中间部分添加计时器,例如,计时10级流的前5级需要多长时间

只需在这些方法中添加计时器,就可以轻松地为中间阶段的各个步骤计时。初始拆分器代码可以测量tryAdvance第一次出现与close()方法之间的时间(它必须向生成的流中添加一个onClose Runnable)。这至少允许流提供库使用计时器,即使它们不知道自己的流是如何被转换和使用的

写这样的东西会很好:

List result = stream
   // stream ops ...etc...etc
   .timeTotalOperation(totalOpTimer) //time between first traverse and close()
   .timePerItemOperation(perItemTimer) //"forEach" timer at this stage
   .collect(Collectors.toList());
但显然,我们不能将这些方法添加到流接口

用委托模式包装流似乎没有任何意义。据我所知,“正确”的实现是利用管道类,它们是不可访问的,并且(可能)会发生更改

我甚至不能将收集器扩展到终端阶段,因为类是最终的或包可见性。虽然我可以滚动自己的收集器并自己调用流(收集器),但是收集器中的所有有用功能都没有了。但是,应该可以编写一个CollectorDelegate类来包装从集合返回的项,例如

List result = stream
   .collect(new TimingCollector(Collectors.toList(), totalOpTimer, perItemTimer));
必须承认,鉴于流用例的复杂性,“每项”的概念是“不确定的”。可能有些操作的“每项”计时根本没有意义。但即使对于流的最简单用例,我也无法找到一种干净的方法来实现这一点

这样一个开放性的问题对于一个好的线程来说提出了太多的问题,所以让我尝试只提出一个。从数据库中读取流,转换为java对象,仅测量从数据库中读取的数据和到java的转换,然后将流转发给消费者以进行更多工作,但不要计算该部分的时间:

import java.util.function.Consumer;
import java.util.stream.Stream;


interface SQLResultSetSupplier {

    default Stream<Object[]> generateStream() {
        return Stream.generate(this::getExpensiveResultSet);
    }
    Object[] getExpensiveResultSet();

    Object expensivelyConvertToJava(Object[] row);
}


public class StreamTimerExample {

    public void example(SQLResultSetSupplier supplier, Consumer<Object> reportConsumer) {
        /**
         * Supplier performs a database query and returns a Stream on the ResultSet.
         * Convert each row of the ResultSet to a Java object.
         * Measure JUST THE ABOVE on a per-item basis.
         *
         * Then send the stream on to a Consumer, e.g., to generate a report.
         * Do NOT measure this second portion.
         */
        Stream<Object[]> baseStream = supplier.generateStream();
        Stream<Object> expensiveOperationStream = baseStream.map(t -> supplier.expensivelyConvertToJava(t)); // measure this
        expensiveOperationStream.forEach(reportConsumer); //don't measure this
    }

}
import java.util.function.Consumer;
导入java.util.stream.stream;
接口SQLResultSetSupplier{
默认流generateStream(){
返回Stream.generate(this::getExpensiveResultSet);
}
对象[]getExpensiveResultSet();
对象expensivelyConvertToJava(对象[]行);
}
公共类StreamTimerExample{
public void示例(SQLResultSetSupplier-supplier、Consumer-reportConsumer){
/**
*供应商执行数据库查询并在结果集上返回流。
*将结果集的每一行转换为Java对象。
*仅在每个项目的基础上测量上述各项。
*
*然后将流发送给消费者,例如生成报告。
*不要测量第二部分。
*/
Stream baseStream=supplier.generateStream();
Stream expensiveOperationStream=baseStream.map(t->supplier.expensivelyConvertToJava(t));//测量此值
expensiveOperationStream.forEach(reportConsumer);//不要对此进行度量
}
}
我的直觉是:你把时间花在了错误的地方


最终,您打算花费大量的时间和精力来实现自己的代码插装

意思:为什么关注“流”?最后,重要的是“最终用户”功能的总体性能。当然,这些数据流可能是其中的一个重要部分。但是你仍然在投入大量的精力来创建可见性。。。对于系统的一个非常特定的“角落”


我建议采用一种不同的策略:而是使用一个分析器来度量端到端用例。然后,您仍然可以(非常容易地)配置探查器,以将测量限制在流操作中。

>最终您打算花费大量时间和精力来实现自己的代码插装。这正是问题所在:它似乎确实需要“大量的时间和精力”。不应该。如果是这样的话,就会丢失一些东西>。重要的是“最终用户”功能的总体性能。当然,这些数据流可能是其中的一个重要部分。你知道有生产级的“反应式APP服务器模式”,你可以考虑“整个应用程序是流”吗?>但是你仍然投入大量精力创造能见度…对于系统的一个非常特定的“角落”。也许我没有解释清楚:我试图监视流中的活动,而不仅仅是流。>而是使用探查器。。。我正在制作。我不能用剖析器。我依赖于一直运行的轻量级指标。>测量端到端用例。假设我们已经在进行端到端用例测量。这些很简单。但在某个时候,你会想知道到底是什么花了这么长时间。
import java.util.function.Consumer;
import java.util.stream.Stream;


interface SQLResultSetSupplier {

    default Stream<Object[]> generateStream() {
        return Stream.generate(this::getExpensiveResultSet);
    }
    Object[] getExpensiveResultSet();

    Object expensivelyConvertToJava(Object[] row);
}


public class StreamTimerExample {

    public void example(SQLResultSetSupplier supplier, Consumer<Object> reportConsumer) {
        /**
         * Supplier performs a database query and returns a Stream on the ResultSet.
         * Convert each row of the ResultSet to a Java object.
         * Measure JUST THE ABOVE on a per-item basis.
         *
         * Then send the stream on to a Consumer, e.g., to generate a report.
         * Do NOT measure this second portion.
         */
        Stream<Object[]> baseStream = supplier.generateStream();
        Stream<Object> expensiveOperationStream = baseStream.map(t -> supplier.expensivelyConvertToJava(t)); // measure this
        expensiveOperationStream.forEach(reportConsumer); //don't measure this
    }

}