Nano Hash - криптовалюты, майнинг, программирование

Задокументированы ли где-либо советы по производительности параллельных коллекций Java (например, для ConcurrentHashMap вызов `get()` перед `putIfAbsent()`)

Суть

В Effective Java (третье издание) автор предоставил совет по повышению производительности в отношении ConcurrentHashMap --- т. е. при использовании existingValue = map.putIfAbsent(key, value) следует сначала вызвать existingValue = map.get(key) и пропустить putIfAbsent(), если ключ уже существует.

Вопрос

Задокументированы ли где-нибудь соображения производительности, подобные упомянутым автором?

Я считаю, что это достаточно важное/фундаментальное соображение производительности, которое должно быть задокументировано где-то официально, тем более что putIfAbsent() уже возвращает значение, если ключ уже существует, что делает дополнительное get() избыточным, и вполне разумно, что кто-то, не знающий о соображении производительности, может отрефакторить проверку get().

Отредактировано, чтобы прояснить вопрос: я не спрашиваю, почему и действительно ли putIfAbsent() всегда более эффективен, но спрашиваю, задокументированы ли где-нибудь такие соображения производительности, учитывая, что API был разработан таким образом, что на простой интерпретации putIfAbsent() и get() используется вместе, как предложено автором без учета соображений производительности, get() кажется излишним.

Подробности

Конкретный пример, приведенный в книге, выглядит следующим образом:

  • предполагая, что мы заинтересованы в реализации метода, похожего на String.intern(), который извлекает значение определенного ключа на карте и, возможно, вставляет значение ключа в карту, если оно еще не существует,
  • тогда более эффективный подход состоит в том, чтобы не использовать предоставленный previousValue = map.putIfAbsent(key, value) напрямую, а предварительно выполнить дополнительную проверку previousValue = map.get(key).

Например, приведенный ниже код менее эффективен:

// Concurrent canonicalizing map atop ConcurrentMap - not optimal
private static final ConcurrentMap<String, String> map = new ConcurrentHashMap<>();
public static String intern(String s) {
    String previousValue = map.putIfAbsent(s, s);
    return previousValue == null ? s : previousValue;
}

Принимая во внимание, что этот фрагмент кода ниже более эффективен:

// Concurrent canonicalizing map atop ConcurrentMap - faster!
public static String intern(String s) {
    String result = map.get(s);
    if (result == null) {
        result = map.putIfAbsent(s, s);
        if (result == null) result = s;
    }
    return result;
}

Причина, указанная автором, заключается в том, что метод get() более оптимизирован, чем putIfAbsent(), что я интерпретирую как означающее, что обычно стоит добавлять дополнительные проверки get(), чтобы время от времени избегать вызова putIfAbsent().

Я бы также предположил, что фактическое влияние на производительность зависит от относительной частоты вставки новых ключей.


  • Похоже, вы полностью упустили из виду, почему они это предложили, и что существуют другие методы, позволяющие сделать это лучше, начиная с Java 8 (computeIfAbsent()). См. Дублированную ссылку, добавленную вверху. --- Подсказка: Дело не в том, что get() более оптимизировано, а в том, что putIfAbsent() потенциально требует создания объекта-значения для помещения в карту, даже если ключ уже есть, что приводит к трате времени на создание этого новый объект значения. Если предоставление этого объекта значения является чем-то большим, чем тривиальным, вы не должны использовать putIfAbsent(). 20.07.2020
  • @Andreas Не могли бы вы точно указать фрагмент кода автора, где происходит создание объекта? Я точно процитировал пример из книги, и я не вижу возможности избежать создания объекта. На самом деле автор ничего не упомянул о медленном создании объектов. Он подчеркнул эффективность get(). Вот что он сказал дословно: ConcurrentHashMap is **optimized for retrieval operations, such as get**. Therefore, it is worth invoking get initially and calling putIfAbsent only if get indicates that it is necessary 20.07.2020
  • Хорошо, извините, putIfAbsent() занимает примерно вдвое больше времени, чем get(), когда ключ уже существует, по крайней мере, для моих тестов на OpenJDK 14, но это может измениться в любое время. и очень специфичен для реализации, поэтому я не верю, что показатели производительности нигде официально не задокументированы. --- Использовать ли get() перед putIfAbsent(), также зависит от ожидаемого соотношения попаданий и от того, заметите ли вы разницу в производительности. 20.07.2020

Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

Как написать эффективное резюме
Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

Как я автоматизирую тестирование с помощью Jest
Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..