[英]JMH - List#addAll faster than ArrayList#new?
I have quite a simple JMH Benchmark, I have read left and right that using the constructor of collections should be faster than using the addAll
method.我有一个非常简单的 JMH Benchmark,我左右阅读过,使用 collections 的构造函数应该比使用
addAll
方法更快。
However, my benchmark tends to prove the contrary.然而,我的基准往往证明相反。
Any explanation on that?对此有何解释?
@State(Scope.Benchmark)
public static class Strings {
public String string = "String";
public List<String> strings = Arrays.asList("String123", "String456", "String789");
}
@Benchmark
@Fork(value = 5, warmups = 3)
public List<String> withStreams(Strings input) {
return Stream.concat(Stream.of(input.string), input.strings.stream())
.collect(Collectors.toList());
}
@Benchmark
@Fork(value = 5, warmups = 3)
public List<String> withoutStreamsButWithConstructor(Strings input) {
List<String> result = new ArrayList<>(input.strings);
result.add(input.string);
return result;
}
@Benchmark
@Fork(value = 5, warmups = 3)
public List<String> withoutStreams(Strings input) {
List<String> result = new ArrayList<>();
result.add(input.string);
result.addAll(input.strings);
return result;
}
PS: The example with Streams is just an experiment (in fact the actual things I wanted to be sure of) PS:Streams的例子只是一个实验(实际上是我想确定的实际事情)
Results结果
# Run complete. Total time: 00:16:31
Benchmark Mode Cnt Score Error Units
App.withStreams thrpt 100 12649053,043 ± 222716,712 ops/s
App.withoutStreams thrpt 100 50572729,531 ± 324271,706 ops/s
App.withoutStreamsButWithConstructor thrpt 100 30179733,201 ± 380273,095 ops/s
Added the following benchmark to the family为家庭添加了以下基准
@Benchmark
@Fork(value = 5, warmups = 3)
public List<String> withoutStreamsWithAddAfter(Strings input) {
List<String> result = new ArrayList<>();
result.addAll(input.strings);
result.add(input.string);
return result;
}
And I now get我现在得到
# Run complete. Total time: 00:22:00
Benchmark Mode Cnt Score Error Units
App.withStreams thrpt 100 13560464,180 ± 201012,539 ops/s
App.withoutStreams thrpt 100 47490197,224 ± 864545,886 ops/s
App.withoutStreamsButWithConstructor thrpt 100 29412182,733 ± 346228,939 ops/s
App.withoutStreamsWithAddAfter thrpt 100 31030909,677 ± 81494,995 ops/s
So the withoutStreams
is the most performant one anyway所以
withoutStreams
无论如何都是性能最好的
I've tried with the following List<String>
我试过以下
List<String>
@State(Scope.Benchmark)
public static class Strings {
public String string = "String";
public List<String> strings = Arrays.asList("String123", "String456", "String789", "StringAbc", "StringDef", "StringGhi", "StringJkl", "StringMno", "StringPqr", "StringStu");
}
And now the results are indeed better for the constructor @Benchmark
现在结果对于构造函数
@Benchmark
确实更好
# Run complete. Total time: 00:22:01
Benchmark Mode Cnt Score Error Units
App.withStreams thrpt 100 7291967,397 ± 330614,125 ops/s
App.withoutStreams thrpt 100 23575768,665 ± 127039,282 ops/s
App.withoutStreamsButWithConstructor thrpt 100 27046342,511 ± 182227,005 ops/s
App.withoutStreamsWithAddAfter thrpt 100 17873682,945 ± 170786,259 ops/s
I suspect the reason lies in how ArrayList is implemented.我怀疑原因在于 ArrayList 是如何实现的。
withoutStreamsButWithConstructor
creates an array of size 1, and when you call addAll
, a new array of size 4 is created, the first element is copied over and the other elements are then added. withoutStreamsButWithConstructor
创建一个大小为 1 的数组,当您调用addAll
时,将创建一个大小为 4 的新数组,复制第一个元素,然后添加其他元素。
withoutStreams
creates an array of size 10 (default capacity) and no resizing is required. withoutStreams
创建一个大小为 10(默认容量)的数组,不需要调整大小。
If strings
contained 10 elements, one array resizing would be required in both methods and I suspect the results would be closer.如果
strings
包含 10 个元素,则两种方法都需要调整一个数组大小,我怀疑结果会更接近。
More generally, if performance is important, sizing the list correctly with the constructor that takes an int
can make a difference.更一般地说,如果性能很重要,那么使用采用
int
的构造函数正确调整列表的大小会有所不同。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.