Skip to content

Performance issue when compared to zstd_safe #266

@fr3akX

Description

@fr3akX

zstd::stream::copy_decode is 10-50x slower then zstd_safe::decompress on my test input. And 2 zstd::stream::copy_decode
40% slower then zstd_safe::decompress.

benchmark:

use criterion::{black_box, criterion_group, criterion_main, Criterion};

fn benchmark(c: &mut Criterion) {
    let data = vec![
        176, 234, 1, 2, 3, 4, 3, 2, 2, 1, 1, 4, 1, 0, 0, 0, 0, 1, 2, 2, 1, 1, 2, 2, 1, 0, 1, 4, 1,
        0, 1, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 1, 0, 1, 4, 3, 4, 1, 0, 0, 0, 1, 2, 2,
        1, 1, 4, 1, 1, 4, 1, 1, 4, 3, 4, 1, 0, 0, 0, 0, 1, 2, 0, 2, 1, 1, 4, 1, 0, 0, 0, 1, 4, 1,
        0, 0, 0, 1, 4, 3, 4, 1, 1, 2, 2, 1, 0, 0, 1, 4, 1, 1, 2, 2, 1, 0, 0, 0, 0, 0, 1, 4, 3, 4,
        1, 0, 0, 1, 4, 1, 0, 0, 1, 4, 1, 0, 1, 4, 1, 1, 2, 0, 0, 0, 0, 2, 1, 1, 4, 1, 0, 0, 0, 0,
        0, 1, 4, 1, 0, 1, 4, 1, 0, 0, 0, 1, 4, 3, 4, 1, 0, 0, 0, 0, 0, 1, 4, 1, 1, 2, 2, 3, 2, 2,
        1, 0, 0, 1, 4, 3, 2, 0, 2, 3, 4, 1, 0, 0, 0, 0, 1, 2, 0, 2, 3, 4, 1, 0, 0, 1, 4, 1, 0, 0,
        0, 0, 0, 0, 0, 0, 1, 4, 1, 0, 1, 4, 1, 0, 0, 0, 0, 1, 4, 1, 0, 0, 1, 4, 1, 1, 4, 1, 0, 0,
        0, 1, 4, 1, 1, 2, 2, 1, 0, 0, 0, 0, 1, 2, 2, 1, 1, 4, 3, 2, 2, 1, 0, 0, 0, 1, 4, 3, 4, 3,
        2, 0, 2, 1, 0, 1, 4, 3, 4, 3, 4, 3, 4, 1, 0, 0, 0, 1, 4, 1, 0, 1, 4, 1, 0, 0, 0, 1, 2, 2,
        1, 0, 0, 0, 1, 2, 2, 3, 2, 2, 1, 1, 4, 1, 0, 0, 0, 0, 0, 0, 0, 1, 4, 1, 0, 0, 0, 1, 4, 1,
        0, 1, 4, 3, 4, 3, 2, 2, 1, 0, 0, 0, 0, 0, 0, 1, 4, 1, 0, 0, 0, 1, 4, 3, 4, 1, 1, 2, 2, 1,
        0, 1, 4, 3, 4, 1, 0, 0, 1, 4, 1, 0, 0, 1, 4, 1, 1, 4, 3, 4, 3, 2, 2, 1, 0, 0, 0, 0, 1, 2,
        2, 1, 1, 4, 1, 0, 0, 0, 1, 4, 1, 1, 4, 1, 1, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
        4, 1, 0, 0, 1, 2, 0, 2, 1, 0, 0, 1, 2, 0, 2, 3, 4, 1, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0, 0,
        0, 0, 0, 1, 4, 1, 0, 0, 1, 4, 1, 1, 4, 3, 4, 3, 4, 1, 1, 4, 1, 0, 0, 0, 0, 0, 0, 1, 4, 1,
        0, 0,
    ];

    let compressed_data = vec![
        40, 181, 47, 253, 96, 225, 0, 45, 6, 0, 116, 3, 176, 234, 1, 2, 3, 4, 3, 2, 2, 1, 1, 4, 1,
        0, 0, 0, 0, 1, 2, 2, 1, 0, 1, 4, 3, 1, 4, 3, 0, 3, 4, 1, 1, 2, 2, 0, 2, 0, 0, 4, 0, 3, 1,
        3, 1, 1, 3, 0, 0, 0, 1, 4, 1, 0, 0, 62, 32, 32, 2, 131, 24, 195, 59, 108, 18, 105, 212,
        128, 114, 129, 138, 11, 139, 55, 80, 2, 158, 238, 53, 218, 1, 142, 0, 149, 16, 0, 76, 113,
        80, 46, 240, 237, 80, 48, 2, 171, 1, 8, 116, 235, 16, 109, 111, 88, 65, 160, 144, 1, 106,
        249, 14, 70, 34, 117, 32, 239, 246, 223, 44, 33, 43, 208, 2, 127, 2, 218, 223, 144, 92,
        192, 213, 1, 49, 0, 21, 228, 160, 132, 171, 183, 221, 153, 13, 7, 173, 12, 140, 50, 104,
        13, 96, 33, 168, 26, 2, 16, 96, 195, 88, 36, 176, 14, 244, 6, 94, 22, 184, 1, 50, 128, 135,
        3, 67, 18, 201, 228, 122, 18, 230, 167, 205, 194, 46, 111, 226, 45, 224, 11, 219, 192, 196,
        8, 91, 51, 12,
    ];

    let mut out = vec![];

    zstd_safe_level(&mut out, &data, 3).unwrap();
    // println!("zstd_safe_level: {:?}", out);
    let mut dec = vec![];
    zstd_safe_decompress(&mut dec, &out).unwrap();
    assert_eq!(data, dec);
    let mut dec2 = vec![];
    decompress_zstd(&mut dec2, &out).unwrap();
    assert_eq!(data, dec2);

    for level in vec![-5, 2, 3, 4, 5] {
        c.bench_function(format!("zstd {}", level).as_str(), |b| {
            b.iter(|| black_box(compress2(&data, level, &mut out)));
        });
        c.bench_function(
            format!("compress_zstd_safe_level {}", level).as_str(),
            |b| {
                b.iter(|| black_box(zstd_safe_level(&mut out, &data, level)));
            },
        );
    }

    c.bench_function("decompress_zstd", |b| {
        b.iter(|| black_box(decompress_zstd(&mut dec, &compressed_data)));
    });
    c.bench_function("zstd_safe_decompress", |b| {
        b.iter(|| black_box(zstd_safe_decompress(&mut dec2, &compressed_data)));
    });
}

pub fn decompress_zstd(dst: &mut Vec<u8>, src: &[u8]) -> codecs::Result<()> {
    zstd::stream::copy_decode(src, dst)?;
    Ok(())
}

fn compress2(data: &[u8], level: i32, out: &mut Vec<u8>) {
    zstd::stream::copy_encode(data, out, level).unwrap();
}

fn zstd_safe_decompress(dst: &mut Vec<u8>, src: &[u8]) -> anyhow::Result<()> {
    let len = dst.len();
    let cap = dst.capacity();

    if cap > len {
        match zstd_safe::decompress(dst, src) {
            Ok(_) => {
                return Ok(());
            }
            Err(_code) => {
                //continue with resize
            }
        }
    }

    let cp = zstd_safe::decompress_bound(src)
        .map_err(|e| anyhow::anyhow!(format!("zstd: {}", zstd_safe::get_error_name(e))))?
        as usize;
    dst.reserve(cp);
    zstd_safe::decompress(dst, src)
        .map_err(|e| anyhow::anyhow!(format!("zstd: {}", zstd_safe::get_error_name(e))))?;
    Ok(())
}

fn zstd_safe_level(dst: &mut Vec<u8>, src: &[u8], level: i32) -> anyhow::Result<()> {
    let len = dst.len();
    let cap = dst.capacity();

    if cap > len {
        match zstd_safe::compress(dst, src, level) {
            Ok(_) => {
                return Ok(());
            }
            Err(_code) => {
                //continue with resize
            }
        }
    }

    let cp = zstd_safe::compress_bound(src.len());

    dst.reserve(cp);
    zstd_safe::compress(dst, src, level)
        .map_err(|e| anyhow::anyhow!(format!("zstd: {}", zstd_safe::get_error_name(e))))?;
    Ok(())
}

criterion_group!(benches, benchmark);
criterion_main!(benches);

results:

zstd -5                                        time:   [1.9967 µs 2.0104 µs 2.0246 µs]
compress_zstd_safe_level -5  time:   [1.8046 µs 1.8093 µs 1.8142 µs]

zstd 2                                         time:   [22.949 µs 23.126 µs 23.310 µs]
compress_zstd_safe_level 2.  time:   [2.2266 µs 2.2318 µs 2.2371 µs]

zstd 3                                       time:   [49.526 µs 50.020 µs 50.530 µs]
compress_zstd_safe_level 3 time:   [1.9953 µs 2.0016 µs 2.0079 µs]

zstd 4                                       time:   [108.99 µs 110.95 µs 113.69 µs]
compress_zstd_safe_level 4 time:   [3.7159 µs 4.3152 µs 5.0711 µs]

zstd 5                                      time:   [145.64 µs 148.66 µs 152.30 µs]
compress_zstd_safe_level 5 time:   [3.3225 µs 3.3312 µs 3.3411 µs]

decompress_zstd.            time:   [1.4370 µs 1.7105 µs 2.0412 µs]
zstd_safe_decompress    time:   [841.48 ns 1.0689 µs 1.3248 µs]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions