- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
基准测试在 intel core i5, Ubuntu
下运行
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
我正在比较 Collectors.counting
和 Collectors.summingLong(x -> 1L)
的性能。这是基准:
public List<Integer> ints = new ArrayList<>();
Collector<Integer, ?, Long> counting = Collectors.counting();
Collector<Integer, ?, Long> summingLong = Collectors.summingLong(x -> 1L);
@Setup
public void setup() throws Exception{
ints.add(new Random().nextInt(1000));
ints.add(new Random().nextInt(1000));
ints.add(new Random().nextInt(1000));
ints.add(new Random().nextInt(1000));
ints.add(new Random().nextInt(1000));
ints.add(new Random().nextInt(1000));
ints.add(new Random().nextInt(1000));
ints.add(new Random().nextInt(1000));
ints.add(new Random().nextInt(1000));
ints.add(new Random().nextInt(1000));
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
public Long counting() {
return ints.stream().collect(counting);
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
public Long summingLong() {
return ints.stream().collect(summingLong);
}
我得到的结果是 Collectors.counting
Collectors.summingLong
慢了 3 倍。
所以我用 -prof perfnorm
用 25 个 fork 运行它。这是结果:
Benchmark Mode Cnt Score Error Units
MyBenchmark.counting avgt 125 87.115 ± 3.787 ns/op
MyBenchmark.counting:CPI avgt 25 0.310 ± 0.011 #/op
MyBenchmark.counting:L1-dcache-load-misses avgt 25 1.963 ± 0.171 #/op
MyBenchmark.counting:L1-dcache-loads avgt 25 258.716 ± 41.458 #/op
MyBenchmark.counting:L1-dcache-store-misses avgt 25 1.890 ± 0.168 #/op
MyBenchmark.counting:L1-dcache-stores avgt 25 131.344 ± 16.433 #/op
MyBenchmark.counting:L1-icache-load-misses avgt 25 0.035 ± 0.007 #/op
MyBenchmark.counting:LLC-loads avgt 25 0.411 ± 0.143 #/op
MyBenchmark.counting:LLC-stores avgt 25 0.007 ± 0.001 #/op
MyBenchmark.counting:branch-misses avgt 25 0.029 ± 0.017 #/op
MyBenchmark.counting:branches avgt 25 139.669 ± 21.901 #/op
MyBenchmark.counting:cycles avgt 25 261.967 ± 29.392 #/op
MyBenchmark.counting:dTLB-load-misses avgt 25 0.036 ± 0.003 #/op
MyBenchmark.counting:dTLB-loads avgt 25 258.111 ± 41.008 #/op
MyBenchmark.counting:dTLB-store-misses avgt 25 0.001 ± 0.001 #/op
MyBenchmark.counting:dTLB-stores avgt 25 131.394 ± 16.166 #/op
MyBenchmark.counting:iTLB-load-misses avgt 25 0.001 ± 0.001 #/op
MyBenchmark.counting:iTLB-loads avgt 25 0.001 ± 0.001 #/op
MyBenchmark.counting:instructions avgt 25 850.262 ± 113.228 #/op
MyBenchmark.counting:stalled-cycles-frontend avgt 25 48.493 ± 8.968 #/op
MyBenchmark.summingLong avgt 125 37.238 ± 0.194 ns/op
MyBenchmark.summingLong:CPI avgt 25 0.311 ± 0.002 #/op
MyBenchmark.summingLong:L1-dcache-load-misses avgt 25 1.793 ± 0.013 #/op
MyBenchmark.summingLong:L1-dcache-loads avgt 25 93.785 ± 0.640 #/op
MyBenchmark.summingLong:L1-dcache-store-misses avgt 25 1.727 ± 0.013 #/op
MyBenchmark.summingLong:L1-dcache-stores avgt 25 56.249 ± 0.408 #/op
MyBenchmark.summingLong:L1-icache-load-misses avgt 25 0.020 ± 0.003 #/op
MyBenchmark.summingLong:LLC-loads avgt 25 0.843 ± 0.117 #/op
MyBenchmark.summingLong:LLC-stores avgt 25 0.004 ± 0.001 #/op
MyBenchmark.summingLong:branch-misses avgt 25 0.008 ± 0.002 #/op
MyBenchmark.summingLong:branches avgt 25 61.472 ± 0.260 #/op
MyBenchmark.summingLong:cycles avgt 25 110.949 ± 0.784 #/op
MyBenchmark.summingLong:dTLB-load-misses avgt 25 0.031 ± 0.001 #/op
MyBenchmark.summingLong:dTLB-loads avgt 25 93.662 ± 0.616 #/op
MyBenchmark.summingLong:dTLB-store-misses avgt 25 ≈ 10⁻³ #/op
MyBenchmark.summingLong:dTLB-stores avgt 25 56.302 ± 0.351 #/op
MyBenchmark.summingLong:iTLB-load-misses avgt 25 0.001 ± 0.001 #/op
MyBenchmark.summingLong:iTLB-loads avgt 25 ≈ 10⁻³ #/op
MyBenchmark.summingLong:instructions avgt 25 357.029 ± 1.712 #/op
MyBenchmark.summingLong:stalled-cycles-frontend avgt 25 10.074 ± 1.096 #/op
我注意到的是:
分支
、说明
、周期
几乎相差 3 倍。还有缓存操作。分支似乎预测得很好,也没有太多缓存未命中(仅我的意见)。
所以问题可能出在编译代码上。使用 -prof perfasm
运行它(太长了,不能放在这里)。
在编译后的代码中,我注意到以下内容:
I. Collectors.summingLong
assembly
我们有 3 个循环遍历数组和计数。首先只计算一个元素
0x00007f9abd226dfd: mov %edi,%ebp ;contains the iteration index
incl %ebp
;...
0x00007f9abd226e27: incl %edi
0x00007f9abd226e29: cmp %ebp,%edi
0x00007f9abd226e2b: jnl 0x7f9abd226e34 ;exit after the first iteration
第二次计算 4 个元素进行 1 次迭代(循环展开了吗?)并且在第一次之后退出。
0x00007f9abd226ea6: add $0x1,%rsi
;...
0x00007f9abd226ed0: add $0x2,%rsi
;...
0x00007f9abd226efa: add $0x3,%rsi
;...
0x00007f9abd226f1c: add $0x4,%rbx
;...
0x00007f9abd226f20: mov %rbx,0x10(%r14)
第三个计算其余元素。
II。 Collectors.counting
assembly
我们只有一个循环来逐个计算所有元素(未展开)。我们还在计数结果的循环内进行了内联装箱转换。此外,我们似乎没有在循环内内联装箱转换
0x00007f80dd22dfb5: mov $0x1,%esi
0x00007f80dd22dfba: nop
0x00007f80dd22dfbb: callq 0x7f80dd046420
这似乎执行 lambda e -> 1L
中提供的 1L
的装箱。但这并不清楚为什么。在执行实际添加时,我们有以下代码:
0x00007f80dd22dfec: mov $0x1,%r10d
0x00007f80dd22dff2: add 0x10(%r11),%r10
此外,我们将计数结果存储在堆栈 mov %r10d,0x10(%rsp)
中,而不是像第一种情况那样存储在堆中。
如果我对正在发生的事情的理解是正确的,我有
问题: 使用装箱转换展开的循环是否导致了 3 倍的减速?如果是这样,为什么运行时不展开 counting
情况下的循环?
请注意 Collectors.summingLong
的 GC 压力比 Collectors.counting
高 2.5。这不是很清楚(我只能猜测我们将中间值存储在 Collectors.counting
的堆栈中)。
MyBenchmark.counting avgt 5 96.956 ± 4.412 ns/op
MyBenchmark.counting:·gc.alloc.rate avgt 5 734.538 ± 33.083 MB/sec
MyBenchmark.counting:·gc.alloc.rate.norm avgt 5 112.000 ± 0.001 B/op
MyBenchmark.counting:·gc.churn.PS_Eden_Space avgt 5 731.423 ± 340.767 MB/sec
MyBenchmark.counting:·gc.churn.PS_Eden_Space.norm avgt 5 111.451 ± 48.411 B/op
MyBenchmark.counting:·gc.churn.PS_Survivor_Space avgt 5 0.017 ± 0.067 MB/sec
MyBenchmark.counting:·gc.churn.PS_Survivor_Space.norm avgt 5 0.003 ± 0.010 B/op
MyBenchmark.counting:·gc.count avgt 5 16.000 counts
MyBenchmark.counting:·gc.time avgt 5 12.000 ms
MyBenchmark.summingLong avgt 5 38.371 ± 1.733 ns/op
MyBenchmark.summingLong:·gc.alloc.rate avgt 5 1856.581 ± 81.706 MB/sec
MyBenchmark.summingLong:·gc.alloc.rate.norm avgt 5 112.000 ± 0.001 B/op
MyBenchmark.summingLong:·gc.churn.PS_Eden_Space avgt 5 1876.736 ± 192.503 MB/sec
MyBenchmark.summingLong:·gc.churn.PS_Eden_Space.norm avgt 5 113.213 ± 9.916 B/op
MyBenchmark.summingLong:·gc.churn.PS_Survivor_Space avgt 5 0.033 ± 0.072 MB/sec
MyBenchmark.summingLong:·gc.churn.PS_Survivor_Space.norm avgt 5 0.002 ± 0.004 B/op
MyBenchmark.summingLong:·gc.count avgt 5 62.000 counts
MyBenchmark.summingLong:·gc.time avgt 5 48.000 ms
最佳答案
我没有查看程序集或对其进行分析,但查看源代码已经提供了一些信息。
summingLong()
这个收集器的结果:
new CollectorImpl<>(
() -> new long[1],
(a, t) -> { a[0] += mapper.applyAsLong(t); },
(a, b) -> { a[0] += b[0]; return a; },
a -> a[0], CH_NOID);
counting()
结果如下:
new CollectorImpl<>(
boxSupplier(identity),
(a, t) -> { a[0] = op.apply(a[0], mapper.apply(t)); },
(a, b) -> { a[0] = op.apply(a[0], b[0]); return a; },
a -> a[0], CH_NOID);
如您所见,counting()
版本还做了一些事情:
op.apply(...)
由于 op
是 Long::sum
,它对基元进行操作,因此涉及大量装箱和拆箱操作。
关于java - 比较 Collectors.summingLong 和 Collectors.counting 的性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48521066/
以下面的列表为例: List input = List.of("FOO", "FOO", "FOO", "FOO", "FOO", "BAR", "BAR", "BAZ", "BAZ", "BAZ",
我有一个类似于下面的类 MyObject . public class MyObject { private String key; // not unique. multiple objec
我想获取按频率键控的数组值频率图。我能够得到相反的 - 按值键控的 map 。尝试切换参数,但 grouping by 不接受 Collector 作为第一个参数。 另一个问题,如何将 Map 实现更
我是 Java 8 Stream API 的新手,但我想用它来解决以下问题。假设我有一个名为 InputRecord 的 POJO,其中包含 name、fieldA 和 fieldB 属性,这些属性可
基准测试在 intel core i5, Ubuntu 下运行 java version "1.8.0_144" Java(TM) SE Runtime Environment (build 1.8.
我尝试使用流和收集器对值进行分组。我有我必须拆分的字符串列表。 我的数据: List stringList = new ArrayList<>(); stringList.add("Key:1,2,3
我想创建一个 Map来自 List的 Points并在映射中使用相同的 parentId 映射列表中的所有条目,例如 Map> . 我用了Collectors.toMap()但它没有编译: Map>
我已经实现了以下示例: Map> map = events.getItems().stream() .collect(Collectors.groupingBy(Event::getS
这个问题在这里已经有了答案: java 8 Collector is not a functional interface, who can tell why? (2 个回答) Java8: Usin
我想通过 Java 8 Stream 和 Collector 接口(interface)将 Map 转换为 ConcurrentHashMap,然后是我可以使用的两个选项。 第一个: Map mb =
如果我有一个对象列表(~200 个元素),只有几个唯一对象(~20 个元素)。 我只想拥有独特的值(value)。之间list.stream().collect(Collectors.toSet())
对于下面开发的 Java 8 代码,我收到以下错误。在此示例中,尝试将 Dish Name 的所有名称连接到一个变量中。使用下面的代码我得到了这个 "The method collect(Collec
我正在尝试使用rook在kubernetes集群上配置ceph,我已经运行了以下命令: kubectl apply -f common.yaml kubectl apply -f operator.y
这是我的流: Stream> futureStream = IntStream .iterate(1, n -> n n++) .mapToObj(pageNumber -> thi
Collector.of(Supplier 供应商、BiConsumer 累加器、BinaryOperator 组合器、Function 完成器、Characterstics...) Coll
按照常规思维,往一个map里put一个已经存在的key,会把原有的key对应的value值覆盖,然而通过一次线上问题,发现Java8中的Collectors.toMap反其道而行之,它默认给抛异常,
我想通过基于 LocalDateTime 的集合进行分组,但我只想获取小时,而不是分钟、秒... .collect(Collectors.groupingBy(cp -> getUpdateLocal
这个问题在这里已经有了答案: What is the point of "final class" in Java? (24 个答案) 关闭 3 年前。 为什么 Collectors 类在 Java
我有一个College具有嵌套静态类的类 Dept 学院 class College { private String collegeName; private Dept dept; public D
在Java库源代码中,Collectors#toList方法的定义如下: public static Collector> toList() { return new CollectorIm
我是一名优秀的程序员,十分优秀!