Самый быстрый способ пошагового чтения большого файла - PullRequest
19 голосов
/ 28 января 2012

Если дан буфер MAX_BUFFER_SIZE и файл, который намного превышает его, как это можно сделать:

  1. Считать файл в блоках MAX_BUFFER_SIZE?
  2. Сделай это как можно быстрее

Я пытался использовать NIO

    RandomAccessFile aFile = new RandomAccessFile(fileName, "r");
    FileChannel inChannel = aFile.getChannel();

    ByteBuffer buffer = ByteBuffer.allocate(CAPARICY);

    int bytesRead = inChannel.read(buffer);

    buffer.flip();

        while (buffer.hasRemaining()) {
            buffer.get();
        }

        buffer.clear();
        bytesRead = inChannel.read(buffer);

    aFile.close();

И обычный IO

    InputStream in = new FileInputStream(fileName);

    long length = fileName.length();

    if (length > Integer.MAX_VALUE) {
        throw new IOException("File is too large!");
    }

    byte[] bytes = new byte[(int) length];

    int offset = 0;

    int numRead = 0;

    while (offset < bytes.length
            && (numRead = in.read(bytes, offset, bytes.length - offset)) >= 0) {
        offset += numRead;
    }

    if (offset < bytes.length) {
        throw new IOException("Could not completely read file " + fileName);
    }

    in.close();

Оказывается, что обычный ввод-вывод примерно в 100 раз быстрее, чем NIO . Я что-то пропустил? Это ожидается? Есть ли более быстрый способ чтения файла в буферных чанках?

В конечном счете, я работаю с большим файлом, у которого нет памяти, чтобы прочитать все сразу. Вместо этого я хотел бы читать его постепенно в блоках, которые затем будут использоваться для обработки.

Ответы [ 2 ]

23 голосов
/ 29 января 2012

Если вы хотите сделать свой первый пример быстрее

FileChannel inChannel = new FileInputStream(fileName).getChannel();
ByteBuffer buffer = ByteBuffer.allocateDirect(CAPACITY);

while(inChannel.read(buffer) > 0)
    buffer.clear(); // do something with the data and clear/compact it.

inChannel.close();

Если вы хотите, чтобы он был еще быстрее.

FileChannel inChannel = new RandomAccessFile(fileName, "r").getChannel();
MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
// access the buffer as you wish.
inChannel.close();

Это может занять 10 - 20 микросекунд для файловдо 2 ГБ.

20 голосов
/ 28 января 2012

Предполагая, что вам нужно прочитать весь файл в память сразу (как вы это делаете в настоящее время), ни чтение небольших кусков, ни NIO вам здесь не помогут.

На самом деле, вы бывероятно, лучше всего читать большие куски - что ваш обычный код ввода-вывода автоматически делает для вас.

Ваш код NIO в настоящее время медленнее, потому что вы читаете только один байт за раз (используя buffer.get();).

Если вы хотите обрабатывать блоками - например, передавать между потоками - вот стандартный способ сделать это без NIO:

InputStream is = ...;
OutputStream os = ...;

byte buffer[] = new byte[1024];
int read;
while((read = is.read(buffer)) != -1){
    os.write(buffer, 0, read);
}

При этом используется размер буфера всего 1 КБ,но может передавать неограниченное количество данных.

(Если вы расширите свой ответ подробностями того, что вы на самом деле хотите сделать на функциональном уровне, я мог бы еще больше улучшить этот ответ.)

...