Эффективная сериализация нативных массивов java с java.io - PullRequest
2 голосов
/ 13 сентября 2011

У меня вопрос к сериализации Java.

Я просто записываю 10 массивов размера int [] array = new int [2 ^ 28] в мой харддик (я знаю, что это довольно много, но яэто необходимо), используя FileOutputStream и BufferedOutputStream в сочетании с Dataoutputstream.Перед каждой сериализацией я создаю новый FileOutputstream и все другие потоки, а затем закрываю и очищаю свои потоки.

Проблема: первая сериализация занимает около 2 секунд, после чего она увеличивается до 17 секунд и остается на этом уровне.В чем здесь проблема?Если я углублюсь в код, то увижу, что FileOutputStreams занимает огромное количество времени для writeByte (...).Это из-за кеширования жесткого диска (полное)?Как я могу избежать этого?Могу ли я очистить его?

Вот мой простой код:

    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

ЗАПУСКRUN 2

15.663881232

ЗАПУСК RUN 3

17.652161328

ЗАПУСК RUN 4

18.020969301

ЗАПУСК RUN 5

11.647542466

НАЧАЛО РАБОТЫ 6

Почему время так сильно увеличивается?

Спасибо,

Eeth

Ответы [ 2 ]

4 голосов
/ 13 сентября 2011

В этой программе я заполняю 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 также может увеличить скорость записи.

0 голосов
/ 13 сентября 2011

Первая запись может просто заполнить кэш вашего жесткого диска, но на самом деле запись на диск пока не производится.

...