Производительность ввода-вывода файлов Java со временем снижается - PullRequest
2 голосов
/ 05 декабря 2008

Я пытаюсь выполнить однократное чтение большого файла (~ 4 ГБ) с использованием Java 5.0 x64 (в Windows XP).

Первоначально скорость чтения файлов была очень высокой, но постепенно пропускная способность существенно снижалась, и моя машина со временем кажется очень не отвечающей.

Я использовал ProcessExplorer для мониторинга статистики файлового ввода-вывода, и похоже, что процесс первоначально читает 500 МБ / с, но эта скорость постепенно падает до примерно 20 МБ / с.

Есть идеи о том, как лучше поддерживать скорость ввода-вывода, особенно при чтении больших файлов с использованием Java?

Вот некоторый тестовый код, который показывает, что «интервал времени» продолжает увеличиваться. Просто передайте Main файл размером не менее 500 МБ.

import java.io.File;
import java.io.RandomAccessFile;

public class MultiFileReader {

public static void main(String[] args) throws Exception {
    MultiFileReader mfr = new MultiFileReader();
    mfr.go(new File(args[0]));
}

public void go(final File file) throws Exception {
    RandomAccessFile raf = new RandomAccessFile(file, "r");
    long fileLength = raf.length();
    System.out.println("fileLen: " + fileLength);
    raf.close();

    long startTime = System.currentTimeMillis();
    doChunk(0, file, 0, fileLength);
    System.out.println((System.currentTimeMillis() - startTime) + " ms");
}

public void doChunk(int threadNum, File file, long start, long end) throws Exception {
    System.out.println("Starting partition " + start + " to " + end);
    RandomAccessFile raf = new RandomAccessFile(file, "r");
    raf.seek(start);

    long cur = start;
    byte buf[] = new byte[1000];
    int lastPercentPrinted = 0;
    long intervalStartTime = System.currentTimeMillis();
    while (true) {
        int numRead = raf.read(buf);
        if (numRead == -1) {
            break;
        }
        cur += numRead;
        if (cur >= end) {
            break;
        }

        int percentDone = (int)(100.0 * (cur - start) / (end - start));
        if (percentDone % 5 == 0) {
            if (lastPercentPrinted != percentDone) {
                lastPercentPrinted = percentDone;
                System.out.println("Thread" + threadNum + " Percent done: " + percentDone + " Interval time: " + (System.currentTimeMillis() - intervalStartTime));
                intervalStartTime = System.currentTimeMillis();
            }
        }
    }
    raf.close();
}
}

Спасибо!

Ответы [ 5 ]

10 голосов
/ 05 декабря 2008

Я очень сомневаюсь, что вы действительно получаете 500 МБ в секунду с вашего диска. Скорее всего, данные кешируются операционной системой, а 20 МБ в секунду - это то, что происходит, когда действительно попадает на диск.

Вполне возможно, что это будет видно в разделе диска диспетчера ресурсов Vista - и нетехнический способ сказать, как слушать диск:)

1 голос
/ 05 декабря 2008

Здесь может оказаться узким местом Java-сборщик мусора.

Я бы сделал буфер больше и приватнее для класса, чтобы он использовался повторно вместо распределения при каждом вызове doChunk ().

public class MultiFileReader {

   private byte buf[] = new byte[256*1024];

   ...

}
1 голос
/ 05 декабря 2008

В зависимости от вашего конкретного оборудования и от того, что еще происходит, вам может потребоваться работать достаточно усердно, чтобы делать намного больше, чем 20 МБ / с.

Я думаю, что, возможно, вы на самом деле не совсем зашкаливаете 500 МБ / с ...

На что вы надеетесь, и проверили ли вы, что ваш конкретный диск даже теоретически способен на это?

0 голосов
/ 05 декабря 2008

Проверить static void read3 () выбрасывает IOException {

        // read from the file with buffering
        // and with direct access to the buffer

        MyTimer mt = new MyTimer();
        FileInputStream fis = 
                     new FileInputStream(TESTFILE);
        cnt3 = 0;
        final int BUFSIZE = 1024;
        byte buf[] = new byte[BUFSIZE];
        int len;
        while ((len = fis.read(buf)) != -1) {
            for (int i = 0; i < len; i++) {
                if (buf[i] == 'A') {
                    cnt3++;
                }
            }
        }
        fis.close();
        System.out.println("read3 time = " 
                                + mt.getElapsed());
    }

от http://java.sun.com/developer/JDCTechTips/2002/tt0305.html

Лучший размер буфера может зависеть от операционной системы. Твой может быть маленький.

0 голосов
/ 05 декабря 2008

Вы можете использовать JConsole для мониторинга вашего приложения, включая использование памяти. 500 МБ / с звучат хорошо, чтобы быть правдой.

Было бы полезно получить дополнительную информацию об используемой реализации и аргументах VM.

...