【问题标题】:How to Reduce the time of GZIPOutputStream如何减少 GZIPOutputStream 的时间
【发布时间】:2019-09-24 01:02:40
【问题描述】:

我尝试压缩一个大(100mb 到 500mb)的 xml 文件。我已经创建了方法 Zip 来做到这一点。问题是它谈论太多时间来压缩。对于 200mb 需要 1.2 秒。我需要将 100mb xml 文件的时间减少 100 毫秒。 如何优化以减少压缩时间?

我通过在压缩比上做出少许妥协来缩短时间。 尝试了另一种算法,如 Snappy、Lz4,但没有太大改进,而且它们的压缩率也很差。据我所知,gzipOutputStream.write() 需要 85% 的时间。所以我怎样才能优化这一步以获得更好的性能而不会影响很多压缩比。

public static String zip(final String str) {
    if ((str == null) || (str.length() == 0)) {
        throw new IllegalArgumentException("Cannot zip null or empty string");
    }

    try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(str.length())) {
        try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream){{def.setLevel(Deflater.BEST_SPEED );}};) {
            gzipOutputStream.write(str.getBytes(StandardCharsets.UTF_8));

        } 
            T5 = System.currentTimeMillis();
            byte[] bytes=byteArrayOutputStream.toByteArray();
             T3 = System.currentTimeMillis();

            String zipped_text=DatatypeConverter.printBase64Binary(bytes);
             T4 = System.currentTimeMillis();
            return zipped_text;

    } catch(IOException e) {
        throw new RuntimeException("Failed to zip content", e);
    }

}

【问题讨论】:

  • 移除压缩,混乱时间——这可能是你的时间渐近线。
  • 您实质上是在问如何编写一段代码,而这段代码绝不会为速度提高 12 倍而优化。答案是:使用针对速度优化的压缩算法。然后你的硬件可能仍然是一个瓶颈。
  • 在每一步中,您总是在处理整个块。 100MB 太大,无法容纳任何 CPU 缓存。因此以约 500KB 的块处理数据并使用流直接重定向输出,例如使用来自 apache commons 编解码器的 Base64OutputStream。
  • @Robert 我是 java 新手,你能提供优化的代码或这个例子吗?谢谢你的时间
  • 不要在内存中做这一切。将其写入目标文件、套接字或其他任何内容。

标签: java gzip gzipoutputstream


【解决方案1】:

这是我的建议:

  1. 创建适当的基准,以便获得可重复的结果。我建议使用基准测试框架;例如JMH.

  2. 分析您的代码/基准,以确定瓶颈/热点在哪里;例如使用 jVisualVM 或 Java Mission Control Flight Recorder。

  3. 使用基准和分析结果来指导您的优化工作。

(出于各种原因,我不会仅仅依赖于对System.currentTimeMillis() 的调用。)

一种可能的解释是,大部分时间都花在了以下步骤中的数据复制上。

  • 创建包含 XML 的输入字符串
  • ByteArrayOutputStream 中捕获压缩字节
  • 将字节组合成另一个字符串。

因此,如果您正在寻找改进方法,请尝试安排一些事情,以便 XML 序列化程序写入通过 gzip 和 base64 转换流式传输数据的管道,然后直接写入文件或套接字流。

另外,如果可能的话,我会避免使用 base64。如果压缩的 XML 在 HTTP 响应中,您应该能够以二进制形式发送它。它会更快,并且产生的网络流量会大大减少。

最后,选择一种在压缩率和压缩时间之间取得良好折衷的压缩算法。


如何在不影响压缩比的情况下优化此步骤以获得更好的性能。

如果您尝试这样做,那么您的目标可能是错误的。 (那你为什么要对压缩文件进行 Base64 编码?这与你的目标相矛盾!)


针对您的 cmets 的更新:

  1. 与将 XML 转换为字符串然后在其上调用 getBytes() 相比,您将(我认为)通过流式传输获得更好的性能。首先,getBytes() 调用是对字符串内容进行不必要的复制。

  2. Lossless Compression 上的 Wikipedia 页面链接到许多算法,其中许多应该具有现成的 Java 实现。此外,它还链接到许多基准。我没有查看基准链接,但我希望至少有一个可以量化不同算法的压缩与计算时间的权衡。

  3. 如果将数据库表从 CLOB 更改为 BLOB:

    • 您可以省去 base64,节省约 25% 的存储空间
    • 您可以省去 base64 编码步骤,节省百分之几的 CPU
    • 然后,您可以选择更快(但不太紧凑)的算法,从而节省更多时间,但代价是您通过转到 BLOB 节省了一些空间。
  4. “我无法真正改变它的业务需求。” - 真的吗?如果数据库模式是业务需求,那么您的业务确实出了问题。另一方面,如果业务在该级别决定技术,那么他们也在决定性能。

    没有可靠的技术理由将压缩数据存储为 CLOB。

  5. 正如有人指出的,获得更快压缩的最简单方法是购买速度更快的计算机。或者(我的想法)一组计算机,以便您可以并行压缩多个文件。

【讨论】:

  • 感谢您的回答,base64 用于将字符串作为 CLOB 存储在数据库中。我无法真正更改它的业务需求。我可以妥协 10% 的压缩比以获得更好的性能,因为我是 Java 新手,请提供与您的答案相关的示例或链接。这对我很有帮助。感谢您的宝贵时间
  • gzipOutputStream.write(str.getBytes(StandardCharsets.UTF_8));正在谈论程序的 85% 时间我该如何优化这一步。
  • @SHUHAIB 要加快这一步,请购买更快的 PC(具有高单线程性能)。压缩是计算密集型的,因此需要一些时间。
  • 虽然总体上的答案肯定是深刻的,但我在这里看到了使用 JMH 的(单一的、具体的)建议,并进行了一些审查:它主要是一个 microbenchmarking 框架,而我的直觉是,尝试将它用于涉及大量 I/O 和大型复杂第三方功能的基准测试可能不是最好的开始。使用 jVisualVM 或 Java Mission Control Flight Recorder 运行的分析器应该更轻松、更快速地在此处提供相关见解。
  • @Marco13 - 这取决于。我怀疑 OP 已经在使用基准测试。但很有可能他还没有完全掌握如何编写基准以提供可靠的结果。使用 JMH 可以消除任何疑问。 (并且没有技术上的理由不使用它......我知道。)
猜你喜欢
  • 2010-10-23
  • 2020-06-05
  • 2012-04-17
  • 2015-06-02
  • 2013-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多