第一章:压缩算法选型——别让OTA卡在解压上
做OTA升级这么多年,我踩过最大的坑,就是压缩算法选型。你想想看,升级包从云端传到设备,传输时间占了OTA总时间的60%以上。压缩算法选对了,升级时间能砍掉一半;选错了,用户等得想砸设备。
今天咱们就聊聊四种主流压缩算法:gzip、zlib、lz4、zstd。我会结合自己的项目经验,帮你理清什么时候该用谁。
1.1 四种算法的核心差异
先看一张对比表,心里有个底:
| 算法 | 压缩率(典型值) | 解压速度(MB/s) | 压缩速度(MB/s) | 内存占用 |
|---|---|---|---|---|
| gzip | 2.5x ~ 3.0x | ~150 | ~50 | 低 |
| zlib | 2.8x ~ 3.5x | ~200 | ~60 | 低 |
| lz4 | 1.8x ~ 2.2x | ~500 | ~400 | 极低 |
| zstd | 3.0x ~ 4.5x | ~300 | ~100 | 中 |
数据来自我在ARM Cortex-A7平台上的实测。注意,不同硬件差异很大,别直接套用。
1.2 压缩率 vs 解压速度:鱼和熊掌
做OTA升级,解压速度比压缩率更重要。为什么?因为压缩是在服务器端做的,服务器CPU强、内存大,慢点没关系。但解压是在设备端,尤其是那些只有几百MHz主频的MCU或低端SoC,解压慢一秒,用户就多等一秒。
我个人习惯这样选:
- 资源极度受限(如MCU、RTOS设备):首选lz4。解压速度极快,内存占用小。我在一个STM32F4的项目里用过,解压速度比gzip快了3倍,虽然压缩包大了20%,但总升级时间反而缩短了。
- 普通Linux设备(如路由器、摄像头):zstd是首选。压缩率比gzip高30%左右,解压速度也快。我建议用zstd的压缩级别3~5,平衡性最好。
- 存储空间极度紧张(如Flash只有2MB):选zlib或gzip。压缩率最高,但要做好解压慢的心理准备。
1.3 硬件加速:别浪费芯片的能力
很多芯片内置了CRC32指令集或硬件压缩引擎。我遇到过不少工程师,明明芯片支持硬件加速,却还在用纯软件解压,白白浪费性能。
举个例子,我在一个全志V3s的项目里,芯片支持NEON指令集和硬件CRC32。用zstd配合硬件CRC32校验,解压速度从280MB/s提升到了410MB/s,提升了近50%。
怎么利用?
- CRC32指令集:很多ARM Cortex-A系列芯片支持。在编译zstd或lz4时,加上
-march=armv8-a+crc或-mcrc32,编译器会自动生成硬件CRC指令。 - NEON/SIMD:用于并行解压。zstd从1.4.0版本开始支持NEON加速,编译时加
-DZSTD_HAVE_NEON=1。 - 专用硬件压缩引擎:有些SoC(如高通、联发科)内置了硬件压缩模块。需要查芯片手册,调用专用驱动。
cat /proc/cpuinfo,看看芯片支持哪些特性。我习惯写一个简单的检测脚本,自动选择最优编译参数。
1.4 避坑指南:我曾经踩过的坑
我曾经在一个智能门锁项目里,选了gzip做OTA压缩。理由是压缩率高,包体小。结果呢?设备端是一颗Cortex-M4,主频只有200MHz。解压一个2MB的升级包,花了整整45秒。用户反馈说「升级像死机一样」。后来换成lz4,解压时间降到8秒,体验完全不一样。
还有一次,我在一个Linux摄像头项目里,用了zstd的最高压缩级别(19级)。压缩率确实漂亮,从4MB压到了1.1MB。但设备端解压时,内存爆了——zstd解压需要约200KB的临时缓冲区,而设备只有128KB可用。嗯,这就是没做资源评估的后果。
1.5 实战代码:快速测试解压速度
我习惯在选型前,先在目标设备上跑一个简单的解压测试。下面是一个用zstd解压的示例:
// 测试zstd解压速度
#include <zstd.h>
#include <sys/time.h>
double get_time_us() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000000.0 + tv.tv_usec;
}
int test_decompress_speed(void* compressed_data, size_t compressed_size,
void* decompressed_buf, size_t decompressed_size) {
double start = get_time_us();
size_t result = ZSTD_decompress(decompressed_buf, decompressed_size,
compressed_data, compressed_size);
double elapsed = (get_time_us() - start) / 1000000.0;
if (ZSTD_isError(result)) {
printf("解压失败: %s\n", ZSTD_getErrorName(result));
return -1;
}
double speed = decompressed_size / elapsed / (1024 * 1024);
printf("解压速度: %.2f MB/s\n", speed);
return 0;
}
这个代码我几乎每个项目都会跑一遍。不同芯片、不同频率,结果差异很大。别信网上的数据,自己测最靠谱。
1.6 总结:我的选型建议
说了这么多,其实就一句话:别只看压缩率,要看总时间。传输时间 + 解压时间,哪个短就用哪个。
我个人现在的标准配置:
- MCU/RTOS设备:lz4,稳如老狗
- Linux设备(有1MB以上RAM):zstd级别3~5,兼顾压缩率和速度
- 存储极度受限:zlib,但要做好解压慢的准备
下一章,咱们聊聊差分升级——怎么只传输变化的部分,让升级包再小一半。