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?

  1. Если я выделю большой кусок (.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. Напротив случая 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 байт * коэффициент переоценки на каждую запись. См. Выше недооценку раздела ключ / значение размера о том, насколько хорошим или плохим он может быть, в зависимости от фактического среднего размера вводимых данных.

Другие вопросы по тегам