Linux压缩算法-zstd

发布时间:2023年12月27日

概述:

本文讲述的是一种快速无损数据压缩算法(zstd)。

ZSTD压缩算法介绍:

Zstandard,简称zstd,是由Facebook开发的一种快速无损压缩算法,针对zlib级的实时压缩场景和更好的压缩比。

ZSTD压缩算法下载:

ubuntu社区的下载地址:http://security.ubuntu.com/ubuntu/pool/main/libz/libzstd/libzstd_1.5.5+dfsg2.orig.tar.xz

github的下载地址:https://github.com/facebook/zstd

ZSTD压缩算法编译:

1、ubuntu(gcc编译):

1.1、直接编译:

1、进入源码文件下;

2、在终端输入“make”;

3、在/lib路径下生成编译的动态库(.so)和静态库(.a)文件,在/programs路径下生成编译的可执行文件;

动态库:libzstd.so、libzstd.so.1、libzstd.so.1.5.5;

静态库:libzstd.a;

可执行文件:zstd;

使用动态库的“libzstd.so、libzstd.so.1”文件时,需要注意,这两个动态库是软连接,需要将其软链接到“libzstd.so.1.5.5”,同时将3个动态库文件移动到需要使用的库文件路径下,如果是需要在c代码中使用,请看下面c代码使用说明。

1.2、编译库文件:

1、进入源码文件下;

2、在ubuntu终端输入以下命令(此处以gcc为例);

mkdir gcc_lib
make lib CC=gcc -j $(grep "cpu cores" /proc/cpuinfo | wc -l)
make install PREFIX=$(pwd)/gcc_lib

命令解析:

make lib CC=gcc -j $(grep "cpu cores" /proc/cpuinfo | wc -l)

设置编译库文件,同时设置gcc编译器和编译内核数;

make install PREFIX=$(pwd)/gcc_lib

设置库安装位置到新建的文件夹;

3、在/gcc_lib/lib路径下生成编译的动态库(.so)和静态库(.a)文件,在/gcc_lib/include路径下生成对应的头文件,在/gcc_lib/bin路径下生成对应的zstd可执行文件;

2、arm(交叉编译库文件):

1、进入源码文件下;

2、在ubuntu终端输入以下命令(此处以rv1126为例);

mkdir arm_lib
make lib CC=arm-linux-gnueabihf-gcc -j $(grep "cpu cores" /proc/cpuinfo | wc -l) 
make install PREFIX=$(pwd)/arm_lib

命令解析:

make lib CC=arm-linux-gnueabihf-gcc -j $(grep "cpu cores" /proc/cpuinfo | wc -l) 

设置编译库文件,同时设置交叉编译器和编译内核数;

make install PREFIX=$(pwd)/arm_lib

设置库安装位置到新建的文件夹;

3、在/arm_lib/lib路径下生成编译的动态库(.so)和静态库(.a)文件,在/arm_lib/include路径下生成对应的头文件,在/arm_lib/bin路径下生成对应的zstd可执行文件;

ZSTD压缩算法使用:

1、可执行文件(zstd):

1.1、可执行文件参数说明:

Usage: zstd [OPTIONS...] [INPUT... | -] [-o OUTPUT]

Options:
  -o OUTPUT                     将输出写入单个文件 OUTPUT.
  -k, --keep                    保存输入文件(s). [默认] 
  --rm                          成功()压缩后删除输入文件.

  -#                            期望的压缩级别,其中' # '是1到19之间的数字;较低的数字提供更快的压缩,较高的数字产生更好							 的压缩比。(默认值:3)

  -d, --decompress              解压.
  -D DICT                       使用DICT作为压缩或解压缩的字典.

  -f, --force                   禁用输入和输出检查。允许覆盖现有文件;从控制台接收输入,将输出打印到STDOUT,以及在链路、  							  块设备等上操作。无法识别的格式按原样通过.

  -h                            显示简洁的帮助信息并退出.
  -H, --help                    显示完整帮助信息并退出.
  -V, --version                 显示版本号并退出.

1.2、可执行文件使用:

1.2.1、数据压缩:

直接使用/programs/zstd的可执行文件进行数据压缩

1、新建测试文件test.txt

2、在bash终端执行压缩命令

指定输出文件:

./zstd -1 ./test.txt -o ./test_zstd.txt

默认输出文件:

./zstd -1 ./test.txt

命令解析:

-1 :表示压缩等级为1,-# 期望的压缩级别,其中“#”是1到19之间的数字;较低的数字提供更快的压缩,较高的数字产生更好的压缩比。(默认值:3);

./test.txt :表示需要压缩的源文件;

-o ./test_zstd.txt : 表示输出到指定的文件(test_zstd.txt)

3、执行结果

指定输出文件:

./test.txt           : 28.18%   (   110 B =>     31 B, ./test_zstd.txt)   

默认输出文件:

./test.txt           : 28.18%   (   110 B =>     31 B, ./test.txt.zst) 

结果分析:

20.18% :表示压缩比例,31/110=0.2818;

./test_zstd.txt :表示指定的压缩输出文件;

./test.txt.zst :表示默认的压缩输出文件;

1.2.2、数据解压:

1、确定需要解压的数据文件(当前使用上面压缩的文件:test_zstd.txt 、test.txt.zst)

2、在终端输入以下命令

指定的需解压文件及指定输出文件

./zstd -d ./test_zstd.txt -o ./test_de.txt

默认格式的需解压文件及不指定输出文件

./zstd -d ./test.txt.zst

3、执行结果

指定的需解压文件及指定输出文件

./test_zstd.txt     : 110 bytes    

默认格式的需解压文件及不指定输出文件

zstd: ./test.txt already exists; overwrite (y/n) ? y
./test.txt.zst      : 110 bytes           

结果分析:

110 bytes :表示解压后的文件大小;

zstd: ./test.txt already exists; overwrite (y/n) :表示当前目录下存在test.txt文件,询问是否需要覆盖;

1.2.3、版本显示:

在终端输入以下命令:

命令1:

./zstd -V

命令2:

./zstd --version

执行结果:

命令1:

*** Zstandard CLI (64-bit) v1.5.5, by Yann Collet ***

命令2:

*** Zstandard CLI (64-bit) v1.5.5, by Yann Collet ***

2、c代码(gcc编译举例):

2.1、库文件移植:

将 gcc_lib 中的 include 和 lib 文件夹拷贝到需要的程序路径下(如:ThirdParty/gcc/zstd_1.5.5/)

2.2、配置cmakelists:

set(ZSTD_INCLUDE_DIRS ThirdParty/gcc/zstd_1.5.5/include)
set(ZSTD_LIBDIR ThirdParty/gcc/zstd_1.5.5/lib)
include_directories(${ZSTD_INCLUDE_DIRS})
link_directories(${ZSTD_LIBDIR})
set(ZSTD_LIBS libzstd.so)

target_link_libraries(${PROJECT_NAME}
        ${ZSTD_LIBS}
        )

2.3、测试代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include "zstd.h"

#define LOG(fmt,arg...)   printf("[%s]" fmt "\r\n",__FUNCTION__,##arg);

long long get_sys_time_us(void)
{
    long long time_us = 0;
    struct timeval sys_current_time;

    gettimeofday(&sys_current_time, NULL);
    time_us = (long long)sys_current_time.tv_sec * 1000000 + sys_current_time.tv_usec;

    return time_us;
}

void zstd_compress_test(void)
{
    // 压缩
    char *src0 = "12345678901234567890123456789012345678901234567890";
    char dst0[64] = {0};
    int src0_size = strlen(src0) + 1;
    int max_dst0_size = sizeof(dst0);
    int dst0_compress_size = 0;

    LOG("before compress = %s, bytes = %d", src0, src0_size);
    if (src0_size < max_dst0_size)
    {
        long long compress_start_time = get_sys_time_us();
        dst0_compress_size = ZSTD_compress(dst0, max_dst0_size, src0, src0_size, 21);
        long long compress_end_time = get_sys_time_us();
        LOG("after compress = %s, bytes = %d", dst0, (int)strlen(dst0));
        LOG("compress_time = %lld us", compress_end_time - compress_start_time);
    }
    else
    {
        LOG("compress error! src0_size >= max_dst0_size");
    }

    // 解压
    char src1[64] = {0};
    char dst1[64] = {0};
    int compressed_size = dst0_compress_size;
    int max_decompressed_size = sizeof(dst1);
    int dst1_decompress_size = 0;

    if (dst0_compress_size < max_decompressed_size)
    {
        memcpy(src1, dst0, dst0_compress_size);
        LOG("before decompress = %s, bytes = %d", src1, (int)strlen(src1));
    }
    else
    {
        LOG("dst0_compress_size >= max_decompressed_size");
    }

    if (compressed_size < max_decompressed_size)
    {
        long long decompress_start_time = get_sys_time_us();
        dst1_decompress_size = ZSTD_decompress(dst1, max_decompressed_size, src1, compressed_size);
        long long decompress_end_time = get_sys_time_us();
        LOG("after decompress = %s, bytes = %d", dst1, dst1_decompress_size);
        LOG("decompress_time = %lld us", decompress_end_time - decompress_start_time);
    }
    else
    {
        LOG("decompress error! compressed_size >= max_decompressed_size");
    }
}

int main(int argc, char *argv[])
{
    LOG("==================================================================================");
    zstd_compress_test();
    return 0;
}

编译后执行可执行文件生成的结果如下

[main]==================================================================================
[zstd_compress_test]before compress = 12345678901234567890123456789012345678901234567890, bytes = 51
[zstd_compress_test]after compress = (�/� 3�, bytes = 7
[zstd_compress_test]compress_time = 3493 us
[zstd_compress_test]before decompress = (�/� 3�, bytes = 7
[zstd_compress_test]after decompress = 12345678901234567890123456789012345678901234567890, bytes = 51
[zstd_compress_test]decompress_time = 415 us

从测试结果可以得知压缩时间是3493us,压缩字节从51个字节压缩成7个字节;解压时间是415us,解压后的数据和压缩前数据一致;

2.4、函数说明:

压缩函数:
ZSTD_compress( void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel);
输入:
    dst:存放压缩后数据的地址;
    dstCapacity:存放压缩后数据的大小;
    src:压缩源数据地址;
    srcSize:压缩源数据大小;
    compressionLevel:压缩等级,0-100 ,默认是3,压缩等级越高压缩效率越高,但时间越长,但到达一定的压缩量后压缩效率会无法提升;

输出:
    压缩成功,返回压缩的数据大小,
    压缩失败,返回错误代码(可以使用ZSTD_isError()进行测试)。

解压函数:
ZSTD_decompress( void* dst, size_t dstCapacity, const void* src, size_t compressedSize);
输入:
    dst:存放解压缩后数据的地址;
    dstCapacity:存放解压缩后数据的大小;
    src:解压缩的源数据地址;
    compressedSize:解压缩源数据大小;

输出:
    解压缩成功,返回解压缩的数据大小,
    解压缩失败,返回错误代码(可以使用ZSTD_isError()进行测试)。

总结:

zstd 压缩算法相对于lz4的压缩时间更快,压缩效率更高一些。

免责声明:本文内容含网络参考、作者编写等,内容版权归原作者所有,未经允许,禁止转载。如涉及作品版权问题,请与我们联系,我们将根据您提供的版权证明材料确认版权并支付稿酬或者删除内容。

文章来源:https://blog.csdn.net/qq_36836950/article/details/135225444
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。