Java: распаковывать файлы в строку слишком медленно - PullRequest
0 голосов
/ 16 мая 2011

Вот как я сжал строку в файл:

public static void compressRawText(File outFile, String src) {
    FileOutputStream fo = null;
    GZIPOutputStream gz = null;
    try {
        fo = new FileOutputStream(outFile);
        gz = new GZIPOutputStream(fo);
        gz.write(src.getBytes());
        gz.flush();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            gz.close();
            fo.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Вот как я его распаковал:

static int BUFFER_SIZE = 8 * 1024;
static int STRING_SIZE = 2 * 1024 * 1024;
public static String decompressRawText(File inFile) {
    InputStream in = null;
    InputStreamReader isr = null;
    StringBuilder sb = new StringBuilder(STRING_SIZE);//constant resizing is costly, so set the STRING_SIZE
    try {
        in = new FileInputStream(inFile);
        in = new BufferedInputStream(in, BUFFER_SIZE);
        in = new GZIPInputStream(in, BUFFER_SIZE);
        isr = new InputStreamReader(in);
        char[] cbuf = new char[BUFFER_SIZE];
        int length = 0;
        while ((length = isr.read(cbuf)) != -1) {
            sb.append(cbuf, 0, length);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            in.close();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }
    return sb.toString();
}

Декомпрессия, кажется, займет вечность. У меня есть ощущение, что я делаю слишком много лишних шагов в бите декомпрессии. Любая идея о том, как я мог бы ускорить это?

РЕДАКТИРОВАТЬ: изменили код к вышеприведенному на основе следующих рекомендаций,
1. Я поменял шаблон, так что просто немного кода, но если я не мог использовать IOUtils это все еще нормально использовать этот шаблон?
2. Я установил для буфера StringBuilder значение 2M, как предложено entonio. Должен ли я установить его немного больше? память все еще в порядке, у меня все еще есть около 10M, как это было предложено монитором кучи от затмения 3. Я вырезал BufferedReader и добавил BufferedInputStream, но я все еще не уверен насчет BUFFER_SIZE, есть предложения?

Приведенная выше модификация улучшила время, необходимое для зацикливания всех моих 30 файлов 2M, с почти 30 секунд до примерно 14, но мне нужно уменьшить его до 10, возможно ли это даже на Android? Хорошо, в основном, мне нужно обработать текстовый файл во всех 60M, я разделил их на 30 2M, и перед тем, как я начну обрабатывать каждую строку, я выполнил вышеприведенную синхронизацию с затратами времени для меня просто зациклить все файлы и получить строку в файле в мою память. Поскольку у меня нет большого опыта, будет ли лучше, если я вместо этого использую 60 из 1М файлов? или любое другое улучшение я должен принять? Спасибо.

ТАКЖЕ: Поскольку физический ввод-вывод довольно трудоемок, и поскольку мои сжатые версии файлов все довольно малы (около 2 КБ из 2 МБ текста), я могу все же сделать выше, но для файла, который уже сопоставлены с памятью? возможно с помощью Java NIO? Спасибо

Ответы [ 3 ]

2 голосов
/ 16 мая 2011

Единственная цель BufferedReader - это метод readLine(), который вы не используете, так почему бы просто не прочитать из InputStreamReader? Также может быть полезно уменьшение размера буфера. Кроме того, вам, вероятно, следует указывать кодировку при чтении и записи, хотя это не должно влиять на производительность.

редактировать: больше данных

Если вы знаете размер строки впереди, вы должны добавить параметр длины к decompressRawText и использовать его для инициализации StringBuilder. В противном случае оно будет постоянно изменяться, чтобы соответствовать результату, и это дорого.

редактировать: уточнение

2 МБ подразумевает много размеров. Нет никакого вреда, если вы укажете емкость, превышающую длину, которую вы получите после чтения (конечно, кроме временного использования большего количества памяти).

0 голосов
/ 16 мая 2011

Добавьте BufferedInputStream между FileInputStream и GZIPInputStream.

Аналогично при записи.

0 голосов
/ 16 мая 2011

Вы должны обернуть FileInputStream BufferedInputStream перед упаковкой GZipInputStream, а не BufferedReader.

Причина в том, что, в зависимости от реализации, любой из различных входных классов в вашей иерархии декораций может решить читать по байтам (и я бы сказал, что InputStreamReader наиболее вероятно сделает это ). И это будет переводиться во многие read(2) звонки, как только он достигнет FileInputStream.

Конечно, это может быть просто суеверием с моей стороны. Но, если вы работаете в Linux, вы всегда можете проверить с помощью strace.


Edit: однажды хороший шаблон, который нужно соблюдать при создании группы потоковых делегатов, это использовать одну переменную InputStream. Тогда у вас есть только одна вещь, которую нужно закрыть в блоке finally (и вы можете использовать Jakarta Commons IOUtils, чтобы избежать множества вложенных блоков try-catch-finally).

  InputStream in = null;
  try
  {
     in = new FileInputStream("foo");
     in = new BufferedInputStream(in);
     in = new GZIPInputStream(in);

     // do something with the stream
  }
  finally
  {
     IOUtils.closeQuietly(in);
  }
...