Задача - сжать / распаковать очень большие данные > 2G , которые не могут быть удержаны одной строкой или массивом ByteArray. Мое решение заключается в записи сжатых / распакованных данных в файл. Это работает, но не достаточно быстро.
Сжатие : текстовый файл -> gzip -> кодировка base64 -> сжатый файл
Распаковка : сжатый файл -> декодирование base64 -> gunzip ->текстовый файл
Результат теста на ноутбуке, с памятью 16G.
Created compressed file, takes 571346 millis
Created decompressed file, takes 378441 millis
Кодовый блок
public static void compress(final InputStream inputStream, final Path outputFile) throws IOException {
try (final OutputStream outputStream = new FileOutputStream(outputFile.toString());
final OutputStream base64Output = Base64.getEncoder().wrap(outputStream);
final GzipCompressorOutputStream gzipOutput = new GzipCompressorOutputStream(base64Output);
final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
reader.lines().forEach(line -> {
try {
gzipOutput.write(line.getBytes());
gzipOutput.write(System.getProperty("line.separator").getBytes());
} catch (final IOException e) {
e.printStackTrace();
}
});
}
}
public static void decompress(final InputStream inputStream, final Path outputFile) throws IOException {
try (final OutputStream outputStream = new FileOutputStream(outputFile.toString());
final GzipCompressorInputStream gzipStream = new GzipCompressorInputStream(Base64.getDecoder().wrap(inputStream));
final BufferedReader reader = new BufferedReader(new InputStreamReader(gzipStream))) {
reader.lines().forEach(line -> {
try {
outputStream.write(line.getBytes());
outputStream.write(System.getProperty("line.separator").getBytes());
} catch (final IOException e) {
e.printStackTrace();
}
});
}
}
Кроме того, я пытался выполнять пакетную запись при отправке данных в файл, особых улучшений не увидел.
# batch write
public static void compress(final InputStream inputStream, final Path outputFile) throws IOException {
try (final OutputStream outputStream = new FileOutputStream(outputFile.toString());
final OutputStream base64Output = Base64.getEncoder().wrap(outputStream);
final GzipCompressorOutputStream gzipOutput = new GzipCompressorOutputStream(base64Output);
final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
StringBuilder stringBuilder = new StringBuilder();
final int chunkSize = Integer.MAX_VALUE / 1000;
String line;
int counter = 0;
while((line = reader.readLine()) != null) {
counter++;
stringBuilder.append(line).append(System.getProperty("line.separator"));
if(counter >= chunkSize) {
gzipOutput.write(stringBuilder.toString().getBytes());
counter = 0;
stringBuilder = new StringBuilder();
}
}
if (counter > 0) {
gzipOutput.write(stringBuilder.toString().getBytes());
}
}
}
Вопрос
- Ищете предложения по ускорению всего процесса
- Какими будут узкие места?
10/2/2019 обновление
Я провел еще несколько тестов, результаты показывают, что узким местом является кодировка base64.
public static void compress(final InputStream inputStream, final Path outputFile) throws IOException {
try (final OutputStream outputStream = new FileOutputStream(outputFile.toString());
final OutputStream base64Output = Base64.getEncoder().wrap(outputStream);
final GzipCompressorOutputStream gzipOutput = new GzipCompressorOutputStream(base64Output)) {
final byte[] buffer = new byte[4096];
int n = 0;
while (-1 != (n = inputStream.read(buffer))) {
gzipOutput.write(buffer, 0, n);
}
}
}
- 2.2G тестовый файл, с 21,5 миллионами строк
- Копировать только файл: ~ 2 секунды
- Только файл Gzip: ~ 12 секунд
- Gzip + base64: ~ 500 секунд