Подтвердить что ты не робот

Guava ImmutableMap имеет значительно более медленный доступ, чем HashMap

Во время работы над эталоном памяти некоторых высокопроизводительных структур данных я понял, что могу использовать ImmutableMap только с небольшим рефакторингом.

Думая, что это будет улучшением, я бросил его в микс и с удивлением обнаружил, что он не только медленнее, чем HashMap, но в однопоточной среде он выглядит медленнее, чем ConcurrentHashMap!

Здесь вы можете увидеть полный эталон: https://bitbucket.org/snippets/dimo414/89K7G

Мясо теста довольно простое, время, необходимое для получения большого количества случайных строк, которые могут существовать на карте.

public static void timeAccess(Map<String,String> map) {
    Random rnd = new Random(seed);
    int foundCount = 0;

    long start = System.nanoTime();

    for(int i = 0; i < loop; i++) {
        String s = map.get(RndString.build(rnd));
        if(s != null)
            foundCount++;
    }

    long stop = System.nanoTime() - start;

    System.out.println("Found "+foundCount+" strings out of "+loop+" attempts - "+
        String.format("%.2f",100.0*foundCount/loop)+" success rate.");
    System.out.println(map.getClass().getSimpleName()+" took "+
        String.format("%.4f", stop/1_000_000_000.0)+" seconds.");
    System.out.println();
}

И работая с HashMap, a ConcurrentHashMap и ImmutableMap, все, содержащие одни и те же значения, последовательно демонстрируют резкое замедление при использовании ImmutableMap - часто более 15% медленнее. Чем более разреженная карта (т.е. Чем чаще map.get() возвращает null), тем больше диспропорция. Здесь результат выполнения пробега:

Found 35312152 strings out of 100000000 attempts - 35.31 success rate.
HashMap took 29.4538 seconds.

Found 35312152 strings out of 100000000 attempts - 35.31 success rate.
ConcurrentHashMap took 32.1465 seconds.

Found 35312152 strings out of 100000000 attempts - 35.31 success rate.
RegularImmutableMap took 37.9709 seconds.

Является ли это документированной/ожидаемой проблемой? Guava Docs указывает, что Immutable*** более эффективен с точки зрения памяти, но ничего не говорит о скорости. Для замедления этой величины я склонен справляться с расходами на память и избегать Immutable***, когда скорость является проблемой (а когда нет?). Я что-то пропустил?

Смотрите также: https://groups.google.com/forum/?fromgroups=#!topic/guava-discuss/I7yPpa5Hlpg

4b9b3361

Ответ 1

Как сказал Луис Вассерман, ImmutableMap не оптимизирован для объектов с медленным методом equals. Я думаю, что основное различие здесь:

HashMap:

if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
    return e.value;

ImmtubleMap:

if (key.equals(candidateKey)) {
    return entry.getValue();

Как вы можете видеть, чтобы проверить наличие столкновений, HashMap сначала проверьте хеши. Это позволяет быстро отклонить значения с разными хэшами. Поскольку String не выполняет эту проверку в методе equals, это делает HashMap быстрее. ImmutableMap не использует эту оптимизацию, потому что это сделает тест медленнее, если equals уже оптимизирован.

Ответ 2

Некоторые возможные причины:

  • Может ли это зависеть от вашей реализации RndString.build()?

  • И посмотрите на реализацию get() обеих карт: com.google.common.collect.RegularImmutableMap.get(Объект) java.util.HashMap.getEntry(Объект) java.util.HashMap сначала пытается сравнить с "==". RegularImmutableMap - нет. Это может ускорить

  • Может ли быть ответственен за другой фактор нагрузки? Возможно, RegularImmutableMap требует больше итераций, чтобы найти правильную запись.