Очень медленная итерация на карте хроники
Я наблюдаю очень медленные итерации по карте хроники - в приведенном ниже примере 93 мс на итерацию по 1 млн записей на моем MacbookPro 2013 года. Мне интересно, есть ли лучший способ итерации или что-то я делаю не так или это ожидается? Я знаю, что Chronicle Map не оптимизирована для итерации, но этот билет, полученный несколько лет назад, заставил меня ожидать гораздо более быстрое время итерации. Пример игрушки ниже:
public static void main(String[] args) throws Exception {
int numEntries = 1_000_000;
int numIterations = 1_000;
int avgEntrySize = BitUtil.SIZE_OF_LONG + BitUtil.SIZE_OF_INT;
ChronicleMap<IntValue, ByteBuffer> map = ChronicleMap.of(IntValue.class, ByteBuffer.class)
.name("test").entries(numEntries).averageValueSize(avgEntrySize)
.putReturnsNull(true).create();
IntValue value = Values.newHeapInstance(IntValue.class);
ByteBuffer buffer = ByteBuffer.allocate(avgEntrySize);
for (int i = 0; i < numEntries; i++) {
value.setValue(i);
buffer.clear();
buffer.putLong(i);
buffer.putInt(i);
buffer.flip();
map.put(value, buffer);
}
System.out.println("Finished insertion");
for (int i = 0; i < numIterations; i++) {
map.forEachEntry(entry -> {
Data<ByteBuffer> data = entry.value();
ByteBuffer val = data.get();
});
}
System.out.println("Finished priming");
long start = System.currentTimeMillis();
for (int i = 0; i < numIterations; i++) {
map.forEachEntry(entry -> {
Data<ByteBuffer> data = entry.value();
ByteBuffer val = data.get();
});
}
System.out.println(
"Elapsed: " + (System.currentTimeMillis() - start) + " for " + numIterations
+ " iterations");
}
Выход: Законченная вставка. Законченная заливка. Прошло: 93327 за 1000 итераций.
1 ответ
Ваши результаты: 93 миллисекунды на 1 миллион ключей в точности совпадают с результатами теста здесь: http://jetbrains.github.io/xodus/, так что он находится в ожидаемом поле. 93 мс / 1 м ключей - это 93 нс на ключ, это "очень медленно" по сравнению с чем? Ваша карта содержит 16 МБ полезной нагрузки, а ее общий размер вне кучи составляет ~ 30 МБ (к вашему сведению, map.offHeapMemoryUsed()
), это намного больше, чем объем памяти L3 в потребительских ноутбуках, поэтому скорость итерации зависит от времени ожидания основной памяти. Итерация Chronicle Map в основном не последовательная, поэтому предварительная выборка из памяти не работает. Я создал проблему по этому поводу.
Также несколько заметок о вашем коде:
- В вашем случае размер значения карты является постоянным, поэтому вы должны использовать
constantValueSizeBySample(ByteBuffer.allocate(12))
вместоaverageValueSize()
, Даже если размер значения карты не был постоянным, предпочтительно использоватьaverageValue()
вместоaverageValueSize()
потому что вы не можете быть уверены, сколько байтов сериализаторы используют для значений. - Ваше значение представляется хорошим вариантом использования для интерфейсов значений с двумя полями. Более того, вы уже используете интерфейс значения в качестве типа ключа -
IntValue
, - Делать тесты, используя JMH