Эффективная сериализация нативных массивов java с java.io
Я должен вопрос о сериализации Java.
Я просто записываю 10 массивов размера int[] array = new int[2^28] в мой harddik (я знаю, что это довольно большой, но мне это нужно так), используя FileOutputStream и BufferedOutputStream в сочетании с Dataoutputstream, Перед каждой сериализацией я создаю новый FileOutputstream и все остальные потоки, а затем закрываю и очищаю свои потоки.
Проблема: первая сериализация занимает около 2 секунд, затем она увеличивается до 17 секунд и остается на этом уровне. В чем здесь проблема? Если я углублюсь в код, то увижу, что FileOutputStreams тратит огромное количество времени на writeByte (...). Это из-за кеширования HDD (полное)? Как я могу избежать этого? Могу ли я очистить это?
Вот мой простой код:
public static void main(String[] args) throws IOException {
System.out.println("### Starting test");
for (int k = 0; k < 10; k++) {
System.out.println("### Run nr ... " + k);
// Creating the test array....
int[] testArray = new int[(int) Math.pow(2, 28)];
for (int i = 0; i < testArray.length; i++) {
if (i % 2 == 0) {
testArray[i] = i;
}
}
BufferedDataOutputStream dataOut = new BufferedDataOutputStream(
new FileOutputStream("e:\\test" + k + "_" + 28 + ".dat"));
// Serializing...
long start = System.nanoTime();
dataOut.write(testArray);
System.out.println((System.nanoTime() - start) / 1000000000.0
+ " s");
dataOut.flush();
dataOut.close();
}
}
где dataOut.write(int[], 0, end)
public void write(int[] i, int start, int len) throws IOException {
for (int ii = start; ii < start + len; ii += 1) {
if (count + 4 > buf.length) {
checkBuf(4);
}
buf[count++] = (byte) (i[ii] >>> 24);
buf[count++] = (byte) (i[ii] >>> 16);
buf[count++] = (byte) (i[ii] >>> 8);
buf[count++] = (byte) (i[ii]);
}
}
и `protected void checkBuf(int need) выдает IOException {
if (count + need > buf.length) {
out.write(buf, 0, count);
count = 0;
}
}`
BufferedDataOutputStream расширяет BufferedOutputStream поставляется вместе со структурой соответствия. Он просто комбинирует BufferedOutputStream с DataOutputStream, чтобы уменьшить количество вызовов методов, когда вы пишете большие массивы (что делает его намного быстрее... до 10 раз...).
Вот вывод:
Начальный тест
НАЧАЛО РАБОТЫ 0
2,001972271
НАЧАЛО РАБОТЫ 1
1,986544604
НАЧАЛО РАБОТЫ 2
+15,663881232
НАЧАЛО РАБОТЫ 3
+17,652161328
НАЧАЛО РАБОТЫ 4
+18,020969301
НАЧАЛО РАБОТЫ 5
+11,647542466
НАЧАЛО РАБОТЫ 6
Почему время так сильно увеличивается?
Спасибо,
Eeth
2 ответа
В этой программе я заполняю 1 ГБ как значения int и "заставляю" их записывать на диск.
String dir = args[0];
for (int i = 0; i < 24; i++) {
long start = System.nanoTime();
File tmp = new File(dir, "deleteme." + i);
tmp.deleteOnExit();
RandomAccessFile raf = new RandomAccessFile(tmp, "rw");
final MappedByteBuffer map = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 1 << 30);
IntBuffer array = map.order(ByteOrder.nativeOrder()).asIntBuffer();
for (int n = 0; n < array.capacity(); n++)
array.put(n, n);
map.force();
((DirectBuffer) map).cleaner().clean();
raf.close();
long time = System.nanoTime() - start;
System.out.printf("Took %.1f seconds to write 1 GB%n", time / 1e9);
}
с каждым файлом, загруженным на диск, они занимают примерно одинаковое количество времени каждый.
Took 7.7 seconds to write 1 GB
Took 7.5 seconds to write 1 GB
Took 7.7 seconds to write 1 GB
Took 7.9 seconds to write 1 GB
Took 7.6 seconds to write 1 GB
Took 7.7 seconds to write 1 GB
Однако, если я закомментирую map.force();
Я вижу этот профиль.
Took 0.8 seconds to write 1 GB
Took 1.0 seconds to write 1 GB
Took 4.9 seconds to write 1 GB
Took 7.2 seconds to write 1 GB
Took 7.0 seconds to write 1 GB
Took 7.2 seconds to write 1 GB
Took 7.2 seconds to write 1 GB
Похоже, что он будет буфер около 2,5 ГБ, что составляет около 10% моей основной памяти, прежде чем он замедлится.
Вы можете очистить кэш, дождавшись окончания предыдущих записей.
Как правило, у вас есть 1 ГБ данных, а скорость записи на диск составляет около 60 МБ / с, что является разумным для жесткого диска SATA. Если вы получаете скорость выше этой, то это потому, что данные на самом деле не записаны на диск и фактически находятся в памяти.
Если вы хотите, чтобы это было быстрее, вы можете использовать отображенный файл памяти. Это дает преимущество записи на диск в фоновом режиме, так как вы заполняете "массив", т. Е. Запись может быть завершена почти сразу, как только вы закончите установку значений.
Другой вариант - получить более быстрый диск. Один 250 ГБ SSD-накопитель может поддерживать скорость записи около 200 МБ / с. Использование нескольких дисков в конфигурации RAID также может увеличить скорость записи.
Первая запись может просто заполнить кэш вашего жесткого диска, фактически не записывая на диск.