OpenHFT ChronicleMap выделение памяти и ограничения
Этот пост, вероятно, будет хорошим кандидатом на часто задаваемые вопросы на OpenHFT.
Я играю с ChronicleMap, рассматривая это как идею, но у меня много вопросов. Я уверен, что большинство начинающих программистов, которые изучают этот продукт, придерживаются аналогичных соображений.
Не могли бы вы объяснить, как память управляется в этом API?
ChronicleMap объявляет о некоторых замечательных ресурсах памяти кучи ТБ, доступных для обработки своих данных, и я хотел бы получить четкое представление об этом.
Давайте перейдем к программисту с ноутбуком 500 ГБ HD и 4 ГБ оперативной памяти. В этом случае чисто математическая система - общий ресурс доступной "замененной" памяти составляет 504 ГБ. Давайте уступим ОС и другим программам половину, и у нас останется 250 ГБ HD и 2 ГБ ОЗУ. Можете ли вы рассказать о фактической доступной памяти, которую ChronicleMap может распределять в числах относительно доступных ресурсов?
Следующие связанные вопросы относятся к реализации ChronicleMap.
Насколько я понимаю, каждый ChronicleMap выделяет часть памяти, с которой он работает, и оптимальная производительность / использование памяти достигается, когда мы можем точно предсказать объем передаваемых данных. Однако это динамичный мир.
Давайте приведем (преувеличенный, но возможный) пример:
Предположим, что карта K (ключ) "города" и их V (значение) - "описание" (городов) и позволяют пользователям большие ограничения на длину описания.
Первый пользователь вводит: K = "Amsterdam"
, V = "City of bicycles"
и эта запись используется для объявления карты - она устанавливает прецедент для пары следующим образом:
ChronicleMap<Integer, PostalCodeRange> cityPostalCodes = ChronicleMap
.of(CharSequence.class, CharSequence.class)
.averageKey("Amsterdam")
.averageValue("City of bicycles")
.entries(5_000)
.createOrRecoverPersistedTo(citiesAndDescriptions);
Теперь следующий пользователь увлекается и пишет анализ о Праге, который он передает: K = "Prague"
, V = "City of 100 towers is located in the hard of Europe ... blah, blah... million words ..."
Теперь программист ожидал максимум 5_000 записей, но это выходит из его рук, и существует много тысяч записей.
ChronicleMap автоматически выделяет память для таких случаев? Если да, есть ли какой-нибудь лучший подход для объявления ChronicleMaps для этого динамического решения? Если нет, вы бы порекомендовали подход (лучше всего в примере кода), как лучше всего обрабатывать такие сценарии?
Как это работает с сохранением в файл?
Могут ли ChronicleMaps истощить мою оперативную память и / или дисковое пространство? Лучшая практика, чтобы избежать этого?
Другими словами, пожалуйста, объясните, как управляется память в случае недооценки и переоценки значений (и / или ключа) длин и количества записей.
Какие из них применимы в ChronicleMap?
- Если я выделю большой кусок (
.entries(1_000_000)
,.averageValueSize(1_000_000)
и фактическое использование - записи = 100, а средний размер значения = 100.
Что просходит?:
1.1. - все работает нормально, но будет большой потраченный впустую кусок - неиспользованный?
1.2. - все работает нормально, неиспользованная память доступна для:
1.2.1 - ChronicleMap
1.2.2 - данный поток использует ChronicleMap
1.2.3 - данный процесс
1.2.4 - данная JVM
1.2.5 - ОС
1.3. - пожалуйста, объясните, если что-то еще происходит с неиспользованной памятью
1.4. - что делает объявление большого размера с моим файлом сохраняемости?
- Напротив случая 1 - выделяю небольшой кусок (
.entries(10)
,.averageValueSize(10)
и фактическое использование составляет 1_000_000s записей, а средний размер значения = 1_000s байтов. Что просходит?:
1 ответ
Давайте перейдем к программисту с ноутбуком 500 ГБ HD и 4 ГБ оперативной памяти. В этом случае чисто математическая система - общий ресурс доступной "замененной" памяти составляет 504 ГБ. Давайте уступим ОС и другим программам половину, и у нас останется 250 ГБ HD и 2 ГБ ОЗУ. Можете ли вы рассказать о фактической доступной памяти, которую ChronicleMap может распределять в числах относительно доступных ресурсов?
В таких условиях Chronicle Map будет работать очень медленно, в среднем 2 случайных чтения и записи на диск (всего 4 операции с произвольным диском) на каждую операцию с Chronicle Map. Традиционные дисковые движки, такие как RocksDB или LevelDB, должны работать лучше, когда размер базы данных намного больше, чем объем памяти.
Теперь программист ожидал максимум 5_000 записей, но это выходит из его рук, и существует много тысяч записей.
ChronicleMap автоматически выделяет память для таких случаев? Если да, есть ли какой-нибудь лучший подход для объявления ChronicleMaps для этого динамического решения? Если нет, вы бы порекомендовали подход (лучше всего в примере кода), как лучше всего обрабатывать такие сценарии?
Хроническая карта будет выделять память до фактического количества вставленных записей, деленного на число, сконфигурированное через ChronicleMappBuilder.entries()
не выше настроенного ChronicleMapBuilder.maxBloatFactor()
, Например если вы создаете карту как
ChronicleMap<Integer, PostalCodeRange> cityPostalCodes = ChronicleMap
.of(CharSequence.class, CharSequence.class)
.averageKey("Amsterdam")
.averageValue("City of bicycles")
.entries(5_000)
.maxBloatFactor(5.0)
.createOrRecoverPersistedTo(citiesAndDescriptions);
Начнёт бросать IllegalStateException
о попытках вставить новые записи, когда размер будет ~ 25 000.
Тем не менее, Chronicle Map работает постепенно медленнее, когда фактический размер значительно превышает заданный размер, поэтому максимально возможный maxBloatFactor()
искусственно ограничен 1000.
Решение прямо сейчас состоит в том, чтобы настроить будущий размер Хронической Карты через entries()
(а также averageKey()
, а также averageValue()
) хотя бы примерно правильно.
Требование о предварительной настройке правдоподобного размера карты хроники признано проблемой удобства использования. Есть способ исправить это, и это находится в дорожной карте проекта.
Другими словами, пожалуйста, объясните, как управляется память в случае недооценки и переоценки значений (и / или ключа) длин и количества записей.
Недооценка размера ключа / значения: пространство теряется в области поиска хеша, ~ 8 байт * коэффициент недооценки, для каждой записи. Таким образом, это может быть довольно плохо, если фактический средний размер записи (ключ + значение) мал, например, 50 байтов, и вы настроили его как 20 байтов, вы потеряете ~ 8 * 50 / 20 = 20 байтов или 40%. Чем больше средний размер входа, тем меньше отходов.
Переоценка размера ключа / значения: если вы настраиваете только средний размер ключа и значения, но не actualChunkSize()
непосредственно, фактический размер чанка автоматически выбирается между 1/8 и 1/4 от среднего размера записи (ключ + значение). Фактический размер чанка - это единица выделения в Chronicle Map. Таким образом, если вы настроили средний размер записи как ~ 1000 байт, фактический размер фрагмента будет выбран в диапазоне от 125 до 250 байт. Если фактический средний размер записи составляет всего 100 байт, вы потеряете много места. Если завышенная оценка мала, ожидаемые потери пространства ограничиваются примерно 20% размера данных.
Поэтому, если вы боитесь, что вы можете переоценить средний размер ключа / значения, настройте actualChunkSize()
в явном виде.
Количество недооценок записей: обсуждено выше. Никаких особых космических отходов, но Хроническая Карта работает медленнее, тем хуже недооценка.
Количество переоценок записей: память теряется в области поиска хешей, ~ 8 байт * коэффициент переоценки на каждую запись. См. Выше недооценку раздела ключ / значение размера о том, насколько хорошим или плохим он может быть, в зависимости от фактического среднего размера вводимых данных.