Почему Collections.synchronizedSet (HashSet) быстрее, чем HashSet для addAll, retainAll и содержит?

1

Я проверил тест, чтобы найти наилучшую параллельную реализацию Set для моей программы с несинхронизированным HashSet в качестве addAll управления и столкнулся с интересным результатом: addAll, retainAll и contains операции для 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");
    }
}
  • 0
    Почему вы думаете, что ваш тест имеет смысл?
Теги:
performance
set
hashset

2 ответа

3
Лучший ответ

Вы не разогреваете JVM.

Обратите внимание, что сначала вы запускаете тест HashSet.

  1. Я немного изменил вашу программу, чтобы запустить тест в цикле 5 раз. SynchronizedSet был быстрее, на моей машине, только в первом тесте.
  2. Затем я попытался изменить порядок двух тестов и только один раз выполнил тест. HashSet снова выиграл.

Подробнее об этом читайте здесь: Как написать правильный микро-тест в Java?

Кроме того, ознакомьтесь с Google Caliper для платформы, которая обрабатывает все эти проблемы с микрообъективом.

0

да попробуйте запустить синхронизацию перед обычным, и вы получите свои "необходимые" результаты. Я считаю, что это связано с разогревом JVM, и больше ничего. Попробуйте предупредить VM с некоторыми вычислениями, а затем запустить тест или запустить его несколько раз в смешанном порядке.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню