Самый быстрый способ написать массив целых чисел в файл на Java?

Как говорится в названии, я ищу самый быстрый способ записи целочисленных массивов в файлы. Массивы будут различаться по размеру и будут реально содержать от 2500 до 25 000 000 ints.

Вот код, который я сейчас использую:

DataOutputStream writer = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(filename)));
for (int d : data)
 writer.writeInt(d);

Учитывая, что DataOutputStream имеет метод записи массивов байтов, я попытался преобразовать массив int в байтовый массив следующим образом:

private static byte[] integersToBytes(int[] values) throws IOException {
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 DataOutputStream dos = new DataOutputStream(baos);
 for (int i = 0; i < values.length; ++i) {
 dos.writeInt(values[i]);
 }
 return baos.toByteArray();
}

и вот так:

private static byte[] integersToBytes2(int[] src) {
 int srcLength = src.length;
 byte[] dst = new byte[srcLength << 2];
 for (int i = 0; i < srcLength; i++) {
 int x = src[i];
 int j = i << 2;
 dst[j++] = (byte) ((x >>> 0) & 0xff);
 dst[j++] = (byte) ((x >>> 8) & 0xff);
 dst[j++] = (byte) ((x >>> 16) & 0xff);
 dst[j++] = (byte) ((x >>> 24) & 0xff);
 }
 return dst;
}

Оба, похоже, дают небольшое увеличение скорости, около 5%. Я не проверял их достаточно строго, чтобы подтвердить это.

Существуют ли какие-либо методы, которые ускорят эту операцию записи файла, или соответствующие руководства по наилучшей практике для производительности записи в IO для Java?

5 ответов

Я рассмотрел три варианта:

  • Использование DataOutputStream;
  • Использование ObjectOutputStream (для Serializable объектов, для которых int[]); и
  • Использование FileChannel.

Результаты

DataOutputStream wrote 1,000,000 ints in 3,159.716 ms
ObjectOutputStream wrote 1,000,000 ints in 295.602 ms
FileChannel wrote 1,000,000 ints in 110.094 ms

Таким образом, версия NIO является самой быстрой. Он также имеет преимущество в разрешении редактирования, то есть вы можете легко изменить один int, тогда как ObjectOutputStream потребует прочтения всего массива, изменения его и записи его в файл.

Код следует:

private static final int NUM_INTS = 1000000;
interface IntWriter {
 void write(int[] ints);
}
public static void main(String[] args) {
 int[] ints = new int[NUM_INTS];
 Random r = new Random();
 for (int i=0; i


Я использовал бы FileChannel из пакета nio и ByteBuffer. Этот подход кажется (на моем компьютере) дает в 2-4 раза лучшую производительность записи:

Выход из программы:

normal time: 2555
faster time: 765

Это программа:

public class Test {
 public static void main(String[] args) throws IOException {
 // create a test buffer
 ByteBuffer buffer = createBuffer();
 long start = System.currentTimeMillis();
 {
 // do the first test (the normal way of writing files)
 normalToFile(new File("first"), buffer.asIntBuffer());
 }
 long middle = System.currentTimeMillis(); 
 {
 // use the faster nio stuff
 fasterToFile(new File("second"), buffer);
 }
 long done = System.currentTimeMillis();
 // print the result
 System.out.println("normal time: " + (middle - start));
 System.out.println("faster time: " + (done - middle));
 }
 private static void fasterToFile(File file, ByteBuffer buffer) 
 throws IOException {
 FileChannel fc = null;
 try {
 fc = new FileOutputStream(file).getChannel();
 fc.write(buffer);
 } finally {
 if (fc != null)
 fc.close();
 buffer.rewind();
 }
 }
 private static void normalToFile(File file, IntBuffer buffer) 
 throws IOException {
 DataOutputStream writer = null;
 try {
 writer = 
 new DataOutputStream(new BufferedOutputStream(
 new FileOutputStream(file)));
 while (buffer.hasRemaining())
 writer.writeInt(buffer.get());
 } finally {
 if (writer != null)
 writer.close();
 buffer.rewind();
 }
 }
 private static ByteBuffer createBuffer() {
 ByteBuffer buffer = ByteBuffer.allocate(4 * 25000000);
 Random r = new Random(1);
 while (buffer.hasRemaining()) 
 buffer.putInt(r.nextInt());
 buffer.rewind();
 return buffer;
 }
}


Основное улучшение, которое вы можете получить для записи int [], - либо:

  • увеличить размер буфера. Размер подходит для большинства потоков, но доступ к файлам может быть быстрее с большим буфером. Это может привести к 10-20% улучшению.

  • Используйте NIO и прямой буфер. Это позволяет записывать 32-битные значения без преобразования в байты. Это может привести к 5% -ному улучшению.

BTW: вы должны иметь возможность писать не менее 10 миллионов значений int в секунду. С кэшированием диска вы увеличиваете это до 200 миллионов в секунду.


Думаю, вам следует использовать файловые каналы (библиотеку java.nio) вместо простых потоков (java.io). Хорошей отправной точкой является интересное обсуждение: Java NIO FileChannel и производительность/полезность FileOutputstream

и соответствующие комментарии ниже.

Ура!


Массив Serializable - не можете ли вы просто использовать writer.writeObject(data);? Это определенно будет быстрее, чем индивидуальные вызовы writeInt.

Если у вас есть другие требования к формату выходных данных, чем поиск в int[], это другой вопрос.

licensed under cc by-sa 3.0 with attribution.