2008-12-04 5 views
2

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

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

Я использовал 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(); 
} 
} 

Спасибо!

+0

Нужна более подробная информация, что вы делаете после прочтения файла? Вы пишете материал на жесткий диск? У вас есть антивирусные сканеры в фоновом режиме? Вы используете буферизованный ввод-вывод? – Pyrolistical 2008-12-04 21:35:53

+0

Сохраняете ли вы какое-либо содержимое файла в памяти? Является ли пространство кучи вашего Java-процесса полным? – 2008-12-04 21:36:20

ответ

10

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

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

+1

Это правильный ответ (как обычно). – StaxMan 2009-05-05 16:51:31

0

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

Дополнительная информация об используемых реализациях и аргументах VM была бы полезной.

+0

Я согласен - 500 МБ/сек звучит слишком хорошо, чтобы быть правдой, но также кажется, что 20 Мбайт/с слишком медленно! Я не работаю с какими-либо специальными аргументами JVM - только по умолчанию для Java 5.0. Я попытаюсь выполнить простой пример кода ниже. – 2008-12-04 21:49:56

1

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

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

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

0

Проверить статической силы 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

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

1

Сборщик мусора Java может быть узким местом здесь.

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

public class MultiFileReader { 

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

    ... 

}