Почему для записи RandomAccessFile writeLong используется несколько вызовов записи? - PullRequest
5 голосов
/ 21 апреля 2011

При профилировании приложения я заметил, что RandomAccessFile.writeLong занимал много времени.

Я проверил код для этого метода, и он включает в себя восемь вызовов нативной записи метода. Я написал альтернативную реализацию для writeLong, используя byte []. Примерно так:

RandomAccessFile randomAccessFile = new RandomAccessFile("out.dat", "rwd");
...
byte[] aux = new byte[8];
aux[0] = (byte) ((l >>> 56) & 0xFF);
aux[1] = (byte) ((l >>> 48) & 0xFF);
aux[2] = (byte) ((l >>> 40) & 0xFF);
aux[3] = (byte) ((l >>> 32) & 0xFF);
aux[4] = (byte) ((l >>> 24) & 0xFF);
aux[5] = (byte) ((l >>> 16) & 0xFF);
aux[6] = (byte) ((l >>> 8) & 0xFF);
aux[7] = (byte) ((l >>> 0) & 0xFF);
randomAccessFile.write(aux);

Я сделал небольшой тест и получил следующие результаты:

Использование writeLong ():
Среднее время вызова: 91 мс

Использование write (byte []):
Среднее время вызова: 11 мс

Пробный запуск на компьютере с Linux с процессором Intel® T2300 @ 1,66 ГГц

Поскольку нативные вызовы имеют некоторое снижение производительности, почему writeLong реализован таким образом? Я знаю, что вопрос должен быть задан, ребята из Sun, но я надеюсь, что у кого-то здесь есть некоторые намеки.

Спасибо.

Ответы [ 2 ]

2 голосов
/ 21 апреля 2011

Я бы проголосовал за лень или (будучи более благотворительным), не думая о последствиях.

Нативная реализация writeLong() потенциально потребовала бы версий для каждой архитектуры, чтобы иметь дело с упорядочением байтов (JNI будетпреобразовать в порядок байтов платформы).Сохраняя перевод в «кроссплатформенном» слое, разработчики упростили работу по переносу.

Что касается того, почему они не преобразовали в массив, в то время как на стороне Java, я подозреваю, что это произошло из-забоязнь сбора мусора.Я предполагаю, что RandomAccessFile изменился минимально с 1.1, и только до 1.3 сборка мусора начала делать небольшие объекты "свободными".

Но , есть альтернативана RandomAccessFile: взгляните на MappedByteBuffer


Редактировать: у меня есть машина с JDK 1.2.2, и с тех пор этот метод не изменился.

2 голосов
/ 21 апреля 2011

Похоже, что RandomAccessFile.writeLong () не минимизирует количество обращений к ОС.Стоимость резко увеличивается при использовании «rwd» вместо «rw», что должно быть достаточно, чтобы указать, что это не сами вызовы, которые стоят времени.(это факт, что операционная система пытается зафиксировать каждую запись на диск, а диск вращается так быстро)

{
    RandomAccessFile raf = new RandomAccessFile("test.dat", "rwd");
    int longCount = 10000;
    long start = System.nanoTime();
    for (long l = 0; l < longCount; l++)
        raf.writeLong(l);
    long time = System.nanoTime() - start;
    System.out.printf("writeLong() took %,d us on average%n", time / longCount / 1000);
    raf.close();
}
{
    RandomAccessFile raf = new RandomAccessFile("test2.dat", "rwd");
    int longCount = 10000;
    long start = System.nanoTime();
    byte[] aux = new byte[8];
    for (long l = 0; l < longCount; l++) {
        aux[0] = (byte) (l >>> 56);
        aux[1] = (byte) (l >>> 48);
        aux[2] = (byte) (l >>> 40);
        aux[3] = (byte) (l >>> 32);
        aux[4] = (byte) (l >>> 24);
        aux[5] = (byte) (l >>> 16);
        aux[6] = (byte) (l >>> 8);
        aux[7] = (byte) l;
        raf.write(aux);
    }
    long time = System.nanoTime() - start;
    System.out.printf("write byte[8] took %,d us on average%n", time / longCount / 1000);
    raf.close();
}

печатает

writeLong() took 2,321 us on average
write byte[8] took 576 us on average

Мне кажется, что у вас нетКэширование записи на диск.Без кэширования диска я бы ожидал, что каждая зафиксированная запись займет около 11 мс для диска 5400 об / мин, т.е. 60000 мс / 5400 => 11 мс.

...