У меня есть приложение с высокой пропускной способностью и низкой задержкой (3000 запросов в секунду, 100 мс на запрос), и мы активно используем Java 8 ConcurrentHashMap
для выполнения поиска. Обычно эти карты обновляются одним фоновым потоком, и несколько потоков читают эти карты.
Я вижу узкое место в производительности, и при профилировании я обнаружил, что ConcurrentHashMap.get
является горячей точкой и занимает большую часть времени.
В другом случае я вижу, что ConcurrentHashMap.computeIfAbsent
является точкой доступа, хотя функция сопоставления имеет очень небольшую задержку, и профиль показывает, что computeIfAbsent
тратит 90% времени на выполнение самого себя и очень меньше времени на выполнение функции сопоставления.
Мой вопрос, есть ли способ улучшить производительность? У меня около 80 потоков одновременно читают из CHM.
computeIfAbsent
оптимистичнымget
, чтобы избежать штрафа за блокировку при чтении. . - person Ben Manes   schedule 24.11.2015computeIfAbsent
, потому что это потенциально изменяющие действия. - person the8472   schedule 24.11.2015computeIfAbsent
пессимистично блокирует перед чтением и, возможно, вычислением значения. На 32-ядерной машине это кажется быстрее в условиях конкуренции, но в остальном оптимистическое чтение намного быстрее. Это особенно верно, когда существует высокая вероятность успешного чтения, например, в кэше. Когда он переворачивается, разница менее заметна, поэтому предварительный просмотр является чистым преимуществом. - person Ben Manes   schedule 24.11.2015get
отображался здесь как точка доступа при одновременном доступе 80 потоков. Параллельные структуры данных часто используют atomics как более дешевую форму синхронизации, но стоимость синхронизации по-прежнему зависит от количества потоков, конкурирующих за одну и ту же строку кэша для атомарной переменной. В структуре непрерывного стиля, такой как CHM, обычно будет общая переменная такого рода для всей структуры. Таким образом, серьезные разногласия здесь могут увеличить стоимость этойget
операции. - person   schedule 24.11.2015concurrent_vector
из Intel Thread Building Blocks (C++). Мы были слишком взволнованы тем фактом, что он разработан для параллелизма и сделал огромные потоки, к которым обращались потоки (как в этом случае). Это стало горячей точкой. Решение: разделить его на несколько векторов с уже существующими знаниями в каждом потоке, используемом для поиска соответствующего. Уменьшите конкуренцию --› большая скорость. Я могу немного ошибаться в архитектурных деталях, почему это помогает, но обычно это работает. - person   schedule 24.11.2015concurrencyLevel
? Похоже, что это имеет тот же эффект, что и разделение его на более мелкие контейнеры («сегменты» в источнике). - person   schedule 24.11.2015get
использует только некоторые непостоянные чтения, которые очень дешевы для x86. А именно, это обычное чтение за вычетом переупорядочения компилятора, т. е. не задействованы инструкции барьера ЦП. При 3k чтений в секунду это не должно быть проблемой. Пока они только читаются, нет когерентности кеша или чего-то подобного, потому что кеш-линии находятся в общем режиме. (Конечно, можно проверить с помощьюperf
или подобных инструментов) - person the8472   schedule 24.11.2015For example, to add a count to a ConcurrentHashMap<String,LongAdder> freqs, you can use freqs.computeIfAbsent(k -> new LongAdder()).increment();
Я тоже использую CHM для обслуживания счетчиков в том же стиле и не вижу здесь никаких проблем с производительностью. - person Amm Sokun   schedule 24.11.2015computeIfAbsent
, который пессимистично получает блокировку. Я посмотрел на CHM#computeIfAbsent, но не смог многого понять - person Amm Sokun   schedule 24.11.2015volatile
. Но параллельная структура такого типа обычно нуждается в каком-то барьере памяти (хотя она может быть свободной от блокировки и по-прежнему использовать атомарные инструкции - это просто перестанет делать ее свободной от ожидания). - person   schedule 24.11.2015else
, где переменнаяf
означает найденную, цепочка корзин используетsynchronized (f)
. Дуг Ли рекомендует оптимизацию предварительной проверки для машин менее чем с 32 путями, но с учетом будущего оптимизация была слишком короткой для внедрения. - person Ben Manes   schedule 24.11.2015Caffeine
, кажется многообещающим, хотелось бы попробовать поскорее!! - person Amm Sokun   schedule 24.11.2015chm.put(key, System.currentTimeMillis()
, и это делается в цикле со средним размером 100 для каждого запроса, я думаю, что лучше было быchm.get(key).setTime(System.currentTimeMillis())
. Что скажите ребята? - person Amm Sokun   schedule 24.11.2015