【问题标题】:Why is Collections.synchronizedSet(HashSet) faster than HashSet for addAll, retainAll, and contains?为什么 Collections.synchronizedSet(HashSet) 对于 addAll、retainAll 和 contains 比 HashSet 快?
【发布时间】:2014-07-31 19:06:00
【问题描述】:

我运行了一个测试来为我的程序找到最佳的并发 Set 实现,使用非同步的 HashSet 作为对照,并遇到了一个有趣的结果:addAllretainAllcontains Collections.synchronizedSet(HashSet) 的操作似乎比常规 HashSet 的操作更快。我的理解是SynchronizedSet(HashSet) 永远不应该比HashSet 快,因为它由带有同步锁的HashSet 组成。我现在已经运行了很多次测试,结果相似。我做错了吗?

相关结果:

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

相关代码:

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");
    }
}

【问题讨论】:

  • 为什么你认为你的测试有意义?

标签: java performance set hashset


【解决方案1】:

您没有在预热 JVM。

请注意,您首先运行HashSet 测试。

  1. 我稍微更改了您的程序以循环运行测试 5 次。 SynchronizedSet 在我的机器上更快,在第一次测试中。
  2. 然后,我尝试颠倒两个测试的顺序,只运行一次测试。 HashSet又赢了。

在此处了解更多信息:How do I write a correct micro-benchmark in Java?

此外,请查看 Google Caliper 了解处理所有这些微基准测试问题的框架。

【讨论】:

    【解决方案2】:

    是的 尝试在常规之前运行同步集,您将获得“需要”的结果。 我认为这与 JVM 热身有关,与其他无关。 尝试通过一些计算警告 VM,然后运行基准测试或以混合顺序运行几次。

    【讨论】:

      猜你喜欢
      • 2017-04-16
      • 2013-04-23
      • 2011-12-13
      • 2023-04-11
      • 2014-12-24
      • 2018-02-18
      • 2023-03-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多