[英]Why is Collections.synchronizedSet(HashSet) faster than HashSet for addAll, retainAll, and contains?
I ran a test to find the best concurrent Set implementation for my program, with a non-synchronized HashSet
as a control, and ran into an interesting result: the addAll
, retainAll
, and contains
operations for a Collections.synchronizedSet(HashSet)
appear to be faster than those of a regular HashSet
. 我运行了一个测试来找到我的程序的最佳并发Set实现,使用非同步的
HashSet
作为控件,并遇到了一个有趣的结果: addAll
, retainAll
和contains
Collections.synchronizedSet(HashSet)
似乎比常规HashSet
快。 My understanding is that a SynchronizedSet(HashSet)
should never be faster than a HashSet
because it consists of a HashSet
with synchronization locks. 我的理解是
SynchronizedSet(HashSet)
永远不应该比HashSet
更快,因为它由带有同步锁的HashSet
组成。 I've run the test quite a few times now, with similar results. 我现在已经进行了几次测试,结果相似。 Am I doing something wrong?
难道我做错了什么?
Relevant results: 相关结果:
Testing set: HashSet
Add: 17.467758 ms
Retain: 28.865039 ms
Contains: 22.18998 ms
Total: 68.522777 ms
--
Testing set: SynchronizedSet
Add: 17.54269 ms
Retain: 20.173502 ms
Contains: 19.618188 ms
Total: 57.33438 ms
Relevant code: 相关代码:
public class SetPerformance {
static Set<Long> source1 = new HashSet<>();
static Set<Long> source2 = new HashSet<>();
static Random rand = new Random();
public static void main(String[] args) {
Set<Long> control = new HashSet<>();
Set<Long> synch = Collections.synchronizedSet(new HashSet<Long>());
//populate sets to draw values from
System.out.println("Populating source");
for(int i = 0; i < 100000; i++) {
source1.add(rand.nextLong());
source2.add(rand.nextLong());
}
//populate sets with initial values
System.out.println("Populating test sets");
control.addAll(source1);
synch.addAll(source1);
testSet(control);
testSet(synch);
}
public static void testSet(Set<Long> set) {
System.out.println("--\nTesting set: " + set.getClass().getSimpleName());
long start = System.nanoTime();
set.addAll(source1);
long add = System.nanoTime();
set.retainAll(source1);
long retain = System.nanoTime();
boolean test;
for(int i = 0; i < 100000; i++) {
test = set.contains(rand.nextLong());
}
long contains = System.nanoTime();
System.out.println("Add: " + (add - start) / 1000000.0 + " ms");
System.out.println("Retain: " + (retain - add) / 1000000.0 + " ms");
System.out.println("Contains: " + (contains - retain) / 1000000.0 + " ms");
System.out.println("Total: " + (contains - start) / 1000000.0 + " ms");
}
}
Note that you run the HashSet
test first. 请注意,您首先运行
HashSet
测试。
SynchronizedSet
was faster, on my machine, in only the first test. SynchronizedSet
增快,我的机器上, 只在第一次测试。 HashSet
won again. HashSet
再次获胜。 Read more about this here: How do I write a correct micro-benchmark in Java? 在这里阅读更多相关内容: 如何在Java中编写正确的微基准测试?
Additionally, check out Google Caliper for a framework that handles all these microbenchmarking issues. 另外,请查看Google Caliper以获取处理所有这些微基准测试问题的框架。
yes try to run the sync set before the regular and you will get your "needed" results. 是尝试在常规之前运行同步设置,您将获得“所需”的结果。 I reckon this has to do with the JVM warm up and nothing else.
我认为这与JVM热身有关,没有别的。 Try to warn up the VM with some computations and then run the benchmark or run it a few times in a mixed order.
尝试通过一些计算警告VM,然后运行基准测试或以混合顺序运行几次。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.