Java String.intern () использует HashTable вместо ConcurrentHashMap - PullRequest
0 голосов
/ 19 мая 2019

Я занимаюсь исследованием String.intern (), и у этого метода есть снижение производительности.Я сравнил String.intern () с ConcurrentHashMap.putIfAbsent (s) с Microbenchmark.Используется Java1.8.0_212, Ubuntu 18.04.2 LTS

@Param({"1", "100", "10000", "1000000"})
private int size;

private StringIntern stringIntern;
private ConcurrentHashMapIntern concurrentHashMapIntern;

@Setup
public void setup(){
    stringIntern = new StringIntern();
    concurrentHashMapIntern = new ConcurrentHashMapIntern();
}
public static class StringIntern{
    public String intern(String s){
        return s.intern();
    }
}
public static class ConcurrentHashMapIntern{
    private final Map<String, String> map;

    public ConcurrentHashMapIntern(){
        map= new ConcurrentHashMap<>();
    }
    public String intern(String s){
        String existString = map.putIfAbsent(s, s);
        return (existString == null) ? s : existString;
    }
}

@Benchmark
public void intern(Blackhole blackhole){
    for(int count =0; count<size; count ++){
        blackhole.consume(stringIntern.intern("Example "+count));
    }
}
@Benchmark
public void concurrentHashMapIntern(Blackhole blackhole){
    for(int count =0; count<size; count++){
        blackhole.consume(concurrentHashMapIntern.intern("Example " +count));
    }
}

Результат, как и ожидалось.ConcurrentHashMap быстрее, чем String.intern () при поиске строки.

Benchmark                             (size)  Mode  Cnt        Score        Error  Units
MyBenchmark.concurrentHashMapIntern        1  avgt    5        0.056 ±      0.007  us/op
MyBenchmark.concurrentHashMapIntern      100  avgt    5        6.094 ±      2.359  us/op
MyBenchmark.concurrentHashMapIntern    10000  avgt    5      787.802 ±    264.179  us/op
MyBenchmark.concurrentHashMapIntern  1000000  avgt    5   136504.010 ±  17872.866  us/op
MyBenchmark.intern                         1  avgt    5        0.129 ±      0.007  us/op
MyBenchmark.intern                       100  avgt    5       13.700 ±      2.404  us/op
MyBenchmark.intern                     10000  avgt    5     1618.514 ±    460.563  us/op
MyBenchmark.intern                   1000000  avgt    5  1027915.854 ± 638910.023  us/op

String.intern () медленнее, чем ConcurrentHashMap, поскольку String.intern () является встроенной реализацией HashTable.А затем прочитайте javadoc о HashTable, эта документация гласит:

Если поточно-ориентированная реализация не требуется, рекомендуется использовать HashMap вместо Hashtable.Если требуется высококонкурентная реализация, ориентированная на многопотоковое исполнение, то вместо Hashtable рекомендуется использовать ConcurrentHashMap.

Это очень запутанная ситуация.Он рекомендует ConcurrentHashMap, но использует HashTable, хотя и снижает производительность.Кто-нибудь имеет представление о том, почему используется собственный экземпляр реализации HashTable ConcurrentHashMap?

1 Ответ

3 голосов
/ 19 мая 2019

Здесь происходит ряд вещей:

  1. У ваших тестов очень большие полосы ошибок.Количество повторов, вероятно, слишком мало.Это делает результаты сомнительными .

  2. Не похоже, что ваши тесты сбрасывают кэши "интернированных строк" после каждого запуска 1 ,Так что это означает, что кэши растут, и каждое повторение будет начинаться с разных условий.Это может объяснить полосу ошибок ...

  3. Ваш ConcurrentHashMap функционально не эквивалентен String::intern.Последний использует собственный эквивалент Reference объектов для обеспечения возможности сбора мусора в интернированных строках.Ваша ConcurrentHashMap реализация не делает.Почему это важно?

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

String.intern () медленнее, чем ConcurrentHashMap, поскольку String.intern () является родной реализацией HashTable.

Нет.Настоящая причина в том, что собственная реализация работает по-другому:

  • При вызове String::intern могут возникнуть накладные расходы на вызов JNI. Внутренние представления различаются.
  • Он должен обрабатывать ссылки, которые влияют на производительность GC.
  • Существуют также скрытые взаимодействия с дедупликацией строк и другими вещами.

Обратите внимание, что эти вещи различаютсязначительно в разных версиях Java.

Это очень запутанная ситуация.Он рекомендует ConcurrentHashMap, но использует HashTable, хотя и снижает производительность.

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

  • Обратите внимание, что String::intern тоже не используетHashTable или HashMap;см. выше.

  • Найденная вами цитата о том, как получить хорошую одновременную производительность из хеш-таблицы.Ваш тест (AFAIK) однопоточный.Для случая последовательного использования HashMap даст лучшую производительность, чем другие.

Кто-нибудь имеет представление о том, почему использовался собственный экземпляр реализации HashTable ConcurrentHashMap?

Он не использует хеш-таблицу;смотри выше.Есть ряд причин, по которым он не HashTable или HashMap или ConcurrentHashMap:

  • Это то, что он уделяет больше внимания использованию памяти.Все реализации хеш-таблиц Java требуют памяти , что делает их непригодными для интернирования строк общего назначения.
  • Значительные затраты памяти и ЦП при использовании классов Reference значительны.
  • Вычисление хэша вновь созданной строки длины N равно O (N), которое будет значимым при интернировании строк, которыеможет быть длиной в сотни / тысячи символов.

Наконец, будьте осторожны, чтобы не сосредоточиться здесь на неправильной проблеме.Если вы пытаетесь оптимизировать стажировку, потому что это является узким местом в вашем приложении, другая стратегия заключается в том, чтобы вообще не стажироваться.На практике это редко экономит память (особенно по сравнению с дедупликацией строк в G1GC) и редко улучшает производительность обработки строк.


В итоге:

  • Вы сравниваете яблоки иапельсины.Ваша реализация на основе карты не эквивалентна нативному интернированию.
  • String::intern не оптимизируется исключительно (даже в первую очередь) для скорости.
  • Фокусируясь на скорости, вы игнорируете использование памяти.и вторичное влияние использования памяти на скорость.
  • Рассмотрим потенциальную оптимизацию вообще не проходить стажировку.

1 - И в нативном intern случай, я не думаю, что это возможно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...