【发布时间】:2019-10-01 22:21:17
【问题描述】:
任务是压缩/解压缩非常大的数据> 2G,单个String或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());
}
}
}
问题
- 寻求有关如何加快整体流程的建议
- 会有哪些瓶颈?
2019 年 10 月 2 日更新
我又做了一些测试,结果表明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 测试文件,2150 万行
- 仅复制文件:~ 2 秒
- 仅 Gzip 文件:~ 12 秒
- Gzip + base64:~ 500 秒
【问题讨论】:
-
这很有趣,我没想到base64部分会那么慢
-
@harold 没错,结果令人惊讶!当我阅读另一篇关于 base64 性能的文章 java-performance.info/base64-encoding-and-decoding-performance 时,结果表明,从 byte[] 到 byte[] 编码 200MB 需要 0.45 秒。在我们的例子中,2.2G 大约需要 5 秒。一定是其他东西让它变慢了,到目前为止我还想不通。
-
我看到如果写入它的流逐字节(或其他小片段)写入 Base64 会很慢,因为转换器喜欢同时转换 3 个字节,但似乎就像 gzip 流甚至不这样做(最终它通过 PendingBuffer),所以我仍然无法解释它
标签: java io compression bigdata