You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让GZIP压缩输出保持恒定的流速度?

问题

我尝试对一个长期运行进程的输出进行流式压缩,该进程的输出速度恒定,但GZIP压缩后的输出速度却不稳定,不清楚原因。

示例代码如下:

import java.io.ByteArrayOutputStream;
import java.util.Random;
import java.util.zip.GZIPOutputStream;

public class Snippet {

    public static void main(final String[] args) throws Exception {
        long startTime = System.currentTimeMillis();
        ByteArrayOutputStream verboseBAOS = new ByteArrayOutputStream() {
            @Override
            public synchronized void write(final byte[] b, final int off, final int len) {
                long milestone = System.currentTimeMillis() - startTime;
                System.out.println("   Wrote (" + len + " bytes) at ms: " + milestone);
                super.write(b, off, len);
            }
        };
        GZIPOutputStream bigChunkOs = new GZIPOutputStream(verboseBAOS, 2048);
        new Thread(() -> {
            try {
                for (int i = 0; i < 20480; i++) {
                    bigChunkOs.write(new Random().nextInt('A', 'z'));
                    if (i % 100 == 0) {
                        System.out.print(".");
                        Thread.sleep(1);
                    }
                }
                bigChunkOs.close();
            } catch (Exception e) {
            }
        }).start();
    }
}

运行输出如下:

Wrote (10 bytes) at ms: 1
..............................................................................................................................................................................   Wrote (2048 bytes) at ms: 207
      Wrote (2048 bytes) at ms: 207
      Wrote (2048 bytes) at ms: 207
      Wrote (2048 bytes) at ms: 207
      Wrote (2048 bytes) at ms: 207
      Wrote (2048 bytes) at ms: 207
      Wrote (367 bytes) at ms: 207
   ...............................   Wrote (2048 bytes) at ms: 243
      Wrote (483 bytes) at ms: 243

输出分为三个阶段:

  1. 头信息(约1ms)
  2. 第一个块(约207ms)
  3. 第二个块(约243ms)

期望输出块更均衡,消除约203ms的间隔,该如何解决?

原因分析

GZIPOutputStream默认会延迟输出压缩数据,必须等内部缓冲区填满,或者调用flush()/close()才会输出压缩结果。你设置了2048字节的缓冲区,但单字节写入的效率极低,加上Thread.sleep(1)的间隔,导致数据积累到缓冲区满需要很长时间,这就出现了长时间无输出、然后一次性批量输出的情况。

另外,Deflate压缩算法本身会积累数据形成压缩块,只有当块大小达标或缓冲区满时才会输出,这也加剧了输出的不平稳。

解决方案

要实现平稳输出,可从以下两点调整:

1. 主动调用flush()强制输出

在每次写入一批数据后,调用bigChunkOs.flush(),让GZIPOutputStream立即输出当前已压缩的数据,不用等缓冲区填满。

修改后的写入逻辑:

for (int i = 0; i < 20480; i++) {
    bigChunkOs.write(new Random().nextInt('A', 'z'));
    if (i % 100 == 0) {
        System.out.print(".");
        // 强制输出当前压缩数据
        bigChunkOs.flush();
        Thread.sleep(1);
    }
}

2. 优化写入方式,减少单字节写入开销

单字节写入会频繁触发内部操作,效率低下。改成批量写入数据,配合flush能让输出更平稳:

Random random = new Random();
byte[] batchBuffer = new byte[100];
for (int i = 0; i < 20480; i += 100) {
    int writeSize = Math.min(100, 20480 - i);
    // 批量生成随机字节
    for (int j = 0; j < writeSize; j++) {
        batchBuffer[j] = (byte) random.nextInt('A', 'z');
    }
    bigChunkOs.write(batchBuffer, 0, writeSize);
    System.out.print(".");
    bigChunkOs.flush();
    Thread.sleep(1);
}

注意事项

频繁调用flush()会让GZIP生成更多独立的压缩块,每个块的压缩上下文会被重置,整体压缩率会略有下降。如果优先保证输出平稳,这个代价是可接受的;如果需要高压缩率,则需要权衡输出间隔和压缩率的平衡。

内容的提问来源于stack exchange,提问作者Grim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 22:58:17