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

如何缩短libzip中zip_close()函数的执行耗时?

libzip v1.8.0 zip_close() 执行耗时过长问题

问题描述

我在应用中使用libzip v1.8.0压缩文件,但zip_close(..)函数的执行耗时过长——例如压缩10个各32MB的文件需要约3分钟。

代码片段

int err = 0;
auto const pZipHandler = ::zip_open(zipPath.c_str(), ZIP_CREATE | ZIP_TRUNCATE, &err);
if (!pZipHandler) {
    PLOGE << "Failed to create a new zip file: " << zipPath;
    PLOGD << fmt::format("Error code: {}, error: {}", err, zip_strerror(pZipHandler));
    PLOGD << "Recordings can still be found in " << recordFolderPath;

    return C2_ERROR_FAILED;
}

for(const auto &entry : std::filesystem::directory_iterator(recordFolderPath)) {
    if(entry.is_regular_file()) {
        const std::string filePath = entry.path().string();
        const std::string fileName = entry.path().filename().string();

        auto const pZipSource = zip_source_file(pZipHandler, filePath.c_str(), 0, 0);
        if (!pZipSource) {
            PLOGE << "Failed to create zip source for file: " << filePath;
            zip_close(pZipHandler);

            return C2_ERROR_FAILED;
        }

        if (zip_file_add(pZipHandler, fileName.c_str(), pZipSource, ZIP_CM_STORE) < 0) {

            PLOGE << "Failed to add file to zip: " << filePath;
            zip_source_free(pZipSource);
            zip_close(pZipHandler);

            return C2_ERROR_FAILED;
        }
    }
}

if(const auto retCode = zip_close(pZipHandler); 0 != retCode) {
    PLOGE << "Failed to close zip file: " << zipPath;
    PLOGD << fmt::format("Error code: {}, error: {}", retCode, zip_strerror(pZipHandler));

    return C2_ERROR_FAILED;
}

待压缩文件列表

root@machine:~# ls -lh /tmp/recordings_1637345115/
-rw-rw-rw-    1 root     root       32.0M Nov 19 18:05 IQ-2000000KHz-0.bin
-rw-rw-rw-    1 root     root       32.0M Nov 19 18:05 IQ-2001000KHz-0.bin
-rw-rw-rw-    1 root     root       32.0M Nov 19 18:05 IQ-2002000KHz-0.bin
-rw-rw-rw-    1 root     root       32.0M Nov 19 18:05 IQ-2003000KHz-0.bin
-rw-rw-rw-    1 root     root       32.0M Nov 19 18:05 IQ-2004000KHz-0.bin
-rw-rw-rw-    1 root     root       32.0M Nov 19 18:05 IQ-2005000KHz-0.bin
-rw-rw-rw-    1 root     root       32.0M Nov 19 18:05 IQ-2006000KHz-0.bin
-rw-rw-rw-    1 root     root       32.0M Nov 19 18:05 IQ-2007000KHz-0.bin
-rw-rw-rw-    1 root     root       32.0M Nov 19 18:05 IQ-2008000KHz-0.bin
-rw-rw-rw-    1 root     root       32.0M Nov 19 18:05 IQ-2009000KHz-0.bin
-rw-rw-rw-    1 root     root       32.0M Nov 19 18:05 IQ-2010000KHz-0.bin

补充信息

  • 运行环境为嵌入式Linux系统。
  • 应用程序与libzip库均已做优化处理。
  • 使用zip命令行工具压缩时表现类似。
  • 耗时接近线性,压缩2个32MB文件约需35秒。
  • 主存储写入速度约为10MB/s。

解决方案

1. 耗时原因分析

10个32MB文件总大小320MB,按10MB/s写入速度计算理论耗时约32秒,但实际耗时达3分钟,说明问题并非单纯的顺序写入速度限制。

libzip在zip_close()阶段会完成:文件内容写入、中央目录结构生成与写入、存储介质同步。结合嵌入式存储特性,核心原因是存储介质随机写入性能差——ZIP格式需要在文件末尾追加中央目录,而嵌入式存储(如eMMC、NAND Flash)的随机写入远慢于顺序写入;同时默认模式下libzip会缓存文件内容,zip_close()时一次性写入引发大量随机IO。

2. 优化措施

(1) 启用即时写入模式

添加ZIP_FLUSH_ON_ADD标志,让zip_file_add()时直接写入文件内容,避免zip_close()阶段批量写入:

if (zip_file_add(pZipHandler, fileName.c_str(), pZipSource, ZIP_CM_STORE | ZIP_FLUSH_ON_ADD) < 0) {
    // 错误处理逻辑不变
}

此操作会将每个文件的内容在添加时立即写入ZIP文件,zip_close()仅需处理中央目录,大幅缩短该阶段耗时。

(2) 预分配ZIP文件空间

嵌入式存储预分配可减少碎片化,提升顺序写入效率。在zip_open()后根据待压缩文件总大小预分配空间:

// 计算待压缩文件总大小
uint64_t totalSize = 0;
for(const auto &entry : std::filesystem::directory_iterator(recordFolderPath)) {
    if(entry.is_regular_file()) {
        totalSize += entry.file_size();
    }
}
// 预分配空间(额外预留64KB用于中央目录)
int fd = open(zipPath.c_str(), O_WRONLY);
if (fd != -1) {
    posix_fallocate(fd, 0, totalSize + 1024 * 64);
    close(fd);
}

(3) 调整存储挂载参数

确保存储介质挂载时使用noatime(禁用访问时间记录)、data=writeback(延迟数据写入)等参数,减少不必要的IO操作:

# 示例挂载命令
mount /dev/mmcblk0p1 /mnt/storage -o noatime,data=writeback

(4) 升级libzip版本

v1.8.0为2021年发布的旧版本,后续版本针对嵌入式IO性能有优化,建议升级至最新稳定版(如v1.10.1)。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 15:23:11