python-zstandard
repository·main·Indexed 20 days ago
https://github.com/indygreg/python-zstandardHigh-performance Python bindings for the Zstandard compression library (version 0.25.0). It provides both C extension and CFFI interfaces, offering one-shot and streaming APIs for compression and decompression. The library includes features for dictionary compression, custom buffer types for efficient memory management, and fine-grained control via ZstdCompressionParameters.
What's inside python-zstandard
- python-zstandard provides Python bindings for the Zstandard compression library. It offers both a C extension and a CFFI interface. The library is designed to provide a rich, Pythonic interface to the underlying Zstandard C API, balancing high performance with the usability and safety expected in Python.
Understand the project status and stability
mainThe project is currently in beta. Users should expect potential backwards incompatible changes before version 1.0 (likely in the 0.9 release), such as renaming the main module from
zstdtozstandardand renaming various types and methods.Best Practice: Pin your package version to prevent unwanted breakage when these changes occur.
Supported platforms include Linux x86_x64 and Windows x86/x86_64 (Python 3.10+). The library provides both C extension and CFFI implementations, with the CFFI bindings being mostly feature complete.
Supported types via the Python Buffer Protocol
mainMany functions in
python-zstandardaccept any Python object that implements the buffer protocol. This allows the library to access the raw bytes of an object without requiring explicit conversion tobytesfirst.Supported types include:
bytesbytearrayarray.arrayio.BytesIOmmap.mmapmemoryview
Performance characteristics and tuning
mainZstandard is highly tunable. Performance characteristics depend on the settings used:
- Default (Level 3): Generally faster and provides better compression ratios than
zlibon most datasets. - Speed-tuned: Approaches
lz4speed and ratios. - Ratio-tuned: Approaches
lzmaratios and compression speed, but with much faster decompression.
Key Performance Features:
- Multi-threading: Supports multi-threaded compression for large inputs.
- Zero-copy: The library provides multiple APIs to facilitate zero-copy operations and minimize Python object creation/garbage collection overhead.
- Throughput: Capable of single-threaded throughputs exceeding 1 GB/s.
To measure performance on your specific hardware, use the
bench.pyscript found in the source code repository.- Default (Level 3): Generally faster and provides better compression ratios than
Decompression requirements for output sizes
mainWhen using one-shot (non-streaming) decompression APIs, you must provide a way to determine the output size. This is because the API requires a pre-allocated buffer to store the result.
To decompress, you must either:
- Ensure the zstd frame header contains the decompressed size.
- Explicitly pass the required output size to the function.
- Specify a maximum output size.
Security Note: The library limits the maximum output size to prevent 'decompression bombs' (e.g., a small input that expands to many gigabytes), which could otherwise exhaust system memory.
Understand Zstandard Frames and Content Size
mainZstandard data is contained within a frame, which includes a header and an optional trailer. The header identifies the data as a zstd frame and describes the compressed content.
To optimize decompression performance, you should store the original content size within the frame. This allows the decompressor to perform a single, exact memory allocation for the output rather than repeatedly growing a buffer.
In
python-zstandard, you can enable this by settingwrite_content_size=Truewhen initializing aZstdCompressor.# Example of enabling content size writing for better decompression performance compressor = zstd.ZstdCompressor(write_content_size=True)Using Dictionaries for better compression
mainA compression dictionary is used to seed the compressor state with common patterns found in your data. This is highly effective when compressing many small, similar objects (e.g., JSON documents with the same structure).
When to use: Dictionary compression is generally only beneficial for small inputs, typically data no larger than a few kilobytes. The effectiveness depends on the similarity between your data and the dictionary used.
Thread and object reuse safety rules
mainTo avoid race conditions and crashes, follow these safety guidelines:
- No Overlapping Operations: A single
ZstdCompressororZstdDecompressorinstance (and any objects derived from them, likeZstdCompressionReader) cannot be used for multiple overlapping operations. You must finish one operation before starting another on the same instance. - No Simultaneous Thread Access: Do not use the same
ZstdCompressororZstdDecompressorinstance in different threads at the same time. - Parallelism via Multiple Instances: If you need to perform multiple compression/decompression tasks in parallel, you must create a separate
ZstdCompressororZstdDecompressorinstance for each thread/task. - Read-Only Assumption: When passing mutable bytes-like objects (e.g.,
bytearray) to compression methods, ensure they are not mutated by another thread while the function is running, as the C extension may release the GIL during the operation.
- No Overlapping Operations: A single
Note on Zstandard's experimental API
mainMany APIs used by this module are marked as experimental within the upstream Zstandard project (e.g., dictionary training).
Because the Zstandard C API's evolution for experimental features is uncertain, the behavior of the underlying C API might change. However,
python-zstandardmitigates this by vendoring and statically linking a specific version of the Zstandard source code. This ensures that the behavior of a specific version ofpython-zstandardremains constant.Recommendation: Pin your version of
python-zstandardto ensure consistent behavior and protection from upstream changes.How Compression and Decompression Contexts work
mainA context holds the configuration (like compression level) and the state for a zstd operation. In this library,
ZstdCompressorandZstdDecompressoract as wrappers around these C API contexts.Performance Tip: Creating and destroying contexts is computationally expensive. You should reuse
ZstdCompressorandZstdDecompressorinstances for multiple operations to gain performance advantages.One-shot vs. Streaming Operations
mainYou can perform compression and decompression using two different modes:
- One-shot (Simple APIs): All input is provided as a single buffer, and the entire output is returned as a single buffer. This is the simplest way to use the library for data that fits comfortably in memory.
- Streaming: Input and output are handled in chunks via multiple function calls. This requires a stream object (a logical extension of a context) to track the state of the ongoing operation. Streaming is necessary for processing data that is too large to fit in memory at once.
How to choose between One-Shot and Streaming APIs
mainThe library provides two main categories of APIs depending on your data size and memory constraints:
One-Shot APIs
- Best for: Small data where the input and output sizes are known.
- Pros: Simple to use (single function call).
- Cons: Both input and output must fit in memory simultaneously. Large inputs can cause long blocking operations.
Streaming APIs
- Best for: Large datasets or data arriving in chunks.
- Pros: Does not require all data to be in memory at once; provides fine-grained control over input/output flow.
- Cons: More complex to implement than one-shot calls.
- Note: When using streaming APIs with file-like or stream objects, be aware that I/O operations (like network or disk access) can cause long pauses in the execution flow.