通常要关注的是如何向java.util.Stream执行的各个部分添加计时指标。在终止时,很容易为整个操作计时,例如(使用codahale库)
try (Context ctx = timer.time()){
stream.count();
}
但是“逐项”计时呢?或者如何在流的中间部分添加计时器,例如,对10级流的前5个级花费多长时间进行计时?
仅通过向那些方法添加计时器,就可以很容易地对中间阶段的各个步骤进行计时。最初的Spliterator代码可以测量首次出现tryAdvance和close()方法之间的时间(它必须向其生成的流中添加onClose Runnable)。这至少允许流提供库使用Timer,即使它们不知道如何对流进行转换和使用也是如此。
写这样的东西真是太好了
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());
但是显然我们不能将这些方法添加到Stream接口。
用委托模式包装Stream似乎没有任何意义。据我所知,“正确”的实现是利用Pipeline类,它们是不可访问的,并且(可能)会发生变化。
由于类是最终的或程序包可见性,我什至无法将收集器扩展到结束阶段。虽然我可以滚动自己的收集器并自己调用stream(Collector),但收集器中有所有有用的功能。但是,应该可以编写一个CollectorDelegate类,该类包装从Collections返回的项目,例如
List result = stream
.collect(new TimingCollector(Collectors.toList(), totalOpTimer, perItemTimer));
考虑到Stream用例的复杂性,必须承认“每个项目”的概念是“ iffy”。在某些操作中,“逐项”计时甚至没有意义。但是,即使对于Streams最简单的用例,我也无法找出一种干净的方法来实现。
这样的开放性问题对于一个好的线程提出了太多的问题,所以让我尝试提出一个问题。从数据库中读取流,转换为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
}
}
答案 0 :(得分:0)
我的胆量在这里:您将时间浪费在了错误的地方。
最后,您打算花费大量时间和精力来实现自己的代码工具。
含义:为什么关注“流”?最后,重要的是“最终用户”功能的整体性能。当然,流可能占其中的很大一部分。但是您仍在投入大量精力来创建可见性...针对系统的非常特定的“角落”。
我会建议一种不同的策略:宁可使用探查器并测量端到端用例。然后,您仍然可以(非常容易地)配置探查器以将测量值限制为流操作。