跳转到主要内容

Blosc的命令行界面和序列化格式

项目描述

作者:

Valentin Hänel

联系:
valentin@haenel.co
列表:

http://groups.google.com/group/blosc

Github:

https://github.com/Blosc/bloscpack

PyPi:

https://pypi.python.org/pypi/bloscpack

Anaconda:

https://anaconda.org/pypi/bloscpack

Ohloh:

https://www.ohloh.net/p/bloscpack

版本:
version
Travis CI:

travis

Coveralls:

coveralls

Python 版本:

pyversions

许可协议:

license

并且…:

powered

描述

Blosc的命令行界面和序列化格式,Blosc是一个高性能、多线程、阻塞和洗牌的压缩器。使用python-blosc绑定与Blosc接口。还包含对高效序列化和反序列化Numpy数组的原生支持。

行为准则

Blosc社区已经采纳了行为准则,我们期望项目参与者遵守。请阅读全文,以便了解哪些行为将被容忍,哪些行为将不被容忍。

依赖项

  • Python 2.7, 3.4, 3.5, 3.6或3.7

  • python-blosc(提供Blosc)和Numpy(在requirements.txt中列出)用于运行代码

  • 用于测试和发布的test_requirements.txt中列出的Python包

文件格式稳定性

该工具被视为alpha阶段、实验性、研究软件。未来压缩文件的内部存储格式可能会改变,这是很常见的。请勿对Bloscpack生成的文件(除非您知道自己在做什么)产生严重依赖。请参阅文件末尾许可中的保证声明。

安装

免责声明:如今安装Python包(及其依赖项)的方法有很多,重复详细说明这些流程是徒劳的。以下是三种已知可行的方法。根据您选择的方法和使用的系统,您可能需要以下任一或全部:超级用户权限、C++编译器和/或虚拟环境。如果您遇到问题或不确定,最好的办法是发送电子邮件到上述邮件列表寻求帮助。

该包在PyPi上可用,因此您可以使用pip安装依赖项和bloscpack本身

$ pip install bloscpack

如果您想直接从GitHub安装,请使用pip的VCS支持

$ pip install git+https://github.com/Blosc/bloscpack

或者,当然,下载源代码或克隆存储库,然后使用标准的setup.py

$ git clone https://github.com/Blosc/bloscpack
$ cd bloscpack
$ python setup.py install

使用

Bloscpack可以通过命令行使用blpk可执行文件访问,它具有多个全局选项和四个子命令:[c | compress][d | decompress][a | append][i | info],其中大多数都有自己的选项。

全局选项和子命令的帮助

$ blpk --help
[...]

每个子命令的帮助

$ blpk compress --help
[...]
$ blpk decompress --help
[...]
$ blpk info --help
[...]
$ blpk append --help
[...]

示例

基础

基本压缩

$ blpk compress data.dat

或者

$ blpk c data.dat

…会将文件data.dat压缩到data.dat.blp

基本解压

$ blpk decompress data.dat.blp data.dcmp

或者

$ blpk d data.dat.blp data.dcmp

…会将文件data.dat.blp解压到文件data.dcmp。如果您省略了[<out_file>]参数,Bloscpack会抱怨说文件data.dat已经存在,并拒绝覆盖它

$ blpk decompress data.dat.blp
blpk: error: output file 'data.dat' exists!

如果您知道自己在做什么,可以使用全局选项[-f | --force]来覆盖覆盖检查

$ blpk --force decompress data.dat.blp

顺便说一下,这也适用于压缩

$ blpk compress data.dat
blpk: error: output file 'data.dat.blp' exists!
$ blpk --force compress data.dat

最后,如果您想使用不同的文件名

$ blpk compress data.dat custom.filename.blp

…会将文件data.dat压缩到custom.filename.blp

设置

默认情况下,Blosc 在压缩和解压缩过程中使用的线程数由系统检测到的核心数决定。您可以使用 [-n | --nthreads] 选项来更改此设置

$ blpk --nthreads 1 compress data.dat

Blosc 的压缩可以通过以下选项控制

  • [-t | --typesize] Blosc 使用的 Typesize(默认:8):$ blpk compress --typesize 8 data.dat

  • [-l | --level] 压缩级别(默认:7):$ blpk compress --level 3 data.dat

  • [-s | --no-shuffle] 禁用洗牌:$ blpk compress --no-shuffle data.dat

  • [-c | --codec] 使用替代编解码器:$ blpk compress --codec lz4 data.dat

此外,还有一些选项可以控制 Bloscpack 文件

  • [-z | --chunk-size] 块的期望近似大小,您可以使用可读字符串,如 8M128Kmax 来使用最大块大小(约 2GB)(默认:1MB):$ blpk compress --chunk-size 128K data.dat$ blpk c -z max data.dat

  • [-k | --checksum <checksum>] 选择要使用的校验和。以下值是允许的:Noneadler32crc32md5sha1sha224sha256sha384sha512(默认:adler32)。正如标题格式所述,每个压缩块都可以存储校验和,这有助于在解压缩时检测损坏:$ blpk compress --checksum crc32 data.dat

  • [-o | --no-offsets] 默认情况下,存储各个块的偏移量。这些偏移量包含在内,以便将来允许部分解压缩。此选项禁用了该功能。此外,预留了一定数量的偏移量(默认:10 * ‘nchunks’),以便将数据附加到文件中:$ blpk compress --no-offsets data.dat

信息子命令

如果您只需要有关文件如何压缩的一些信息 [i | info]

$ blpk info data.dat.blp
blpk: BloscpackHeader:
blpk:     format_version: 3
blpk:     offsets: True
blpk:     metadata: False
blpk:     checksum: 'adler32'
blpk:     typesize: 8
blpk:     chunk_size: 1.0M (1048576B)
blpk:     last_chunk: 900.0K (921600B)
blpk:     nchunks: 1526
blpk:     max_app_chunks: 15260
blpk: 'offsets':
blpk: [134320,459218,735869,986505,1237646,...]
blpk: First chunk blosc header:
blpk: OrderedDict([('version', 2), ('versionlz', 1), ('flags', 1), ('typesize', 8), ('nbytes', 1048576), ('blocksize', 131072), ('ctbytes', 324894)])
blpk: First chunk blosc flags:
blpk: OrderedDict([('byte_shuffle', True), ('pure_memcpy', False), ('bit_shuffle', False), ('split_blocks', False), ('codec', 'blosclz')])

重要的是,标题和标志信息仅适用于第一个块。通常这不会成问题,因为 bloscpack 压缩文件往往具有类似的设置,如使用的编解码器、typesize 等。然而,没有任何东西可以阻止您使用不同的设置将数据追加到现有的 bloscpack 文件中。例如,文件的一半可能使用‘blosclz’压缩,而文件的另一半可能使用‘lz4’压缩。在任何情况下,请注意,输出应被视为对所有块可能正确的指示,但不一定是必然的。

添加元数据

使用 [-m | --metadata] 选项,您可以将文件中的 JSON 包括在内

$ cat meta.json
{"dtype": "float64", "shape": [200000000], "container": "numpy"}
$ blpk compress --chunk-size=512M --metadata meta.json data.dat
$ blpk info data.dat.blp
blpk: BloscpackHeader:
blpk:     format_version: 3
blpk:     offsets: True
blpk:     metadata: True
blpk:     checksum: 'adler32'
blpk:     typesize: 8
blpk:     chunk_size: 512.0M (536870912B)
blpk:     last_chunk: 501.88M (526258176B)
blpk:     nchunks: 3
blpk:     max_app_chunks: 30
blpk: 'offsets':
blpk: [922,78074943,140783242,...]
blpk: 'metadata':
blpk: {   u'container': u'numpy', u'dtype': u'float64', u'shape': [200000000]}
blpk: MetadataHeader:
blpk:     magic_format: 'JSON'
blpk:     meta_options: '00000000'
blpk:     meta_checksum: 'adler32'
blpk:     meta_codec: 'zlib'
blpk:     meta_level: 6
blpk:     meta_size: 59.0B (59B)
blpk:     max_meta_size: 590.0B (590B)
blpk:     meta_comp_size: 58.0B (58B)
blpk:     user_codec: ''

在解压缩时会打印出来

$ blpk decompress data.dat.blp
blpk: Metadata is:
blpk: '{u'dtype': u'float64', u'shape': [200000000], u'container': u'numpy'}'

追加

您还可以将数据追加到现有的 bloscpack 压缩文件中

$ blpk append data.dat.blp data.dat

然而,在追加数据量方面存在一些限制。例如,如果存在偏移量部分,必须有足够的空间来存储追加块偏移量。如果没有偏移量,则可以在由最大块数和块大小限制的情况下尽可能追加更多数据。此外,对压缩选项也有限制。例如,无法更改所使用的校验和。但是,可以更改追加块的压缩级别、类型大小和洗牌选项。

请注意,截至 v0.5.0,追加仍然被视为实验性的。

详细和调试模式

最后,有两个互斥的选项用于控制输出量。

第一个会打印基本信息,[-v | --verbose]

$ blpk --verbose compress --chunk-size 0.5G data.dat
blpk: using 4 threads
blpk: getting ready for compression
blpk: input file is: 'data.dat'
blpk: output file is: 'data.dat.blp'
blpk: input file size: 1.49G (1600000000B)
blpk: nchunks: 3
blpk: chunk_size: 512.0M (536870912B)
blpk: last_chunk_size: 501.88M (526258176B)
blpk: output file size: 198.39M (208028617B)
blpk: compression ratio: 7.691250
blpk: done

… 而 [-d | --debug] 会打印详细的运行情况

$ blpk --debug compress --chunk-size 0.5G data.dat
blpk: command line argument parsing complete
blpk: command line arguments are:
blpk:     force: False
blpk:     verbose: False
blpk:     offsets: True
blpk:     checksum: adler32
blpk:     subcommand: compress
blpk:     out_file: None
blpk:     metadata: None
blpk:     cname: blosclz
blpk:     in_file: data.dat
blpk:     chunk_size: 536870912
blpk:     debug: True
blpk:     shuffle: True
blpk:     typesize: 8
blpk:     clevel: 7
blpk:     nthreads: 4
blpk: using 4 threads
blpk: getting ready for compression
blpk: input file is: 'data.dat'
blpk: output file is: 'data.dat.blp'
blpk: input file size: 1.49G (1600000000B)
blpk: nchunks: 3
blpk: chunk_size: 512.0M (536870912B)
blpk: last_chunk_size: 501.88M (526258176B)
blpk: BloscArgs:
blpk:     typesize: 8
blpk:     clevel: 7
blpk:     shuffle: True
blpk:     cname: 'blosclz'
blpk: BloscpackArgs:
blpk:     offsets: True
blpk:     checksum: 'adler32'
blpk:     max_app_chunks: <function <lambda> at 0x1182de8>
blpk: metadata_args will be silently ignored
blpk: max_app_chunks is a callable
blpk: max_app_chunks was set to: 30
blpk: BloscpackHeader:
blpk:     format_version: 3
blpk:     offsets: True
blpk:     metadata: False
blpk:     checksum: 'adler32'
blpk:     typesize: 8
blpk:     chunk_size: 512.0M (536870912B)
blpk:     last_chunk: 501.88M (526258176B)
blpk:     nchunks: 3
blpk:     max_app_chunks: 30
blpk: raw_bloscpack_header: 'blpk\x03\x01\x01\x08\x00\x00\x00 \x00\x10^\x1f\x03\x00\x00\x00\x00\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00'
blpk: Handle chunk '0'
blpk: checksum (adler32): '\x1f\xed\x1e\xf4'
blpk: chunk handled, in: 512.0M (536870912B) out: 74.46M (78074017B)
blpk: Handle chunk '1'
blpk: checksum (adler32): ')\x1e\x08\x88'
blpk: chunk handled, in: 512.0M (536870912B) out: 59.8M (62708295B)
blpk: Handle chunk '2' (last)
blpk: checksum (adler32): '\xe8\x18\xa4\xac'
blpk: chunk handled, in: 501.88M (526258176B) out: 64.13M (67245997B)
blpk: Writing '3' offsets: '[296, 78074317, 140782616]'
blpk: Raw offsets: '(\x01\x00\x00\x00\x00\x00\x00\xcdQ\xa7\x04\x00\x00\x00\x00\x18,d\x08\x00\x00\x00\x00'
blpk: output file size: 198.39M (208028617B)
blpk: compression ratio: 7.691250
blpk: done

Python API

Bloscpack 具有一个灵活而简单的 API,由一系列“参数”对象和高级函数组成,这些函数可以根据您的输入和输出需求进行调用。

在命名法上,Python 3 为 Bloscpack 做了很多工作,因为我们始终需要故意将压缩数据表示为字节。这使得区分文本(如文件名)和二进制字节对象(如压缩数据)变得更加容易和自然。

参数

三种参数类型是

  • BloscArgs

  • BloscpackArgs

  • MetadataArgs

bloscpack/args.py 中定义。实例化任何一个都会创建一个带有默认设置的对象。默认值在 bloscpack/defaults.py 中定义。您可以在以下列出的高级函数中使用这些参数。

您可以通过传递相应的关键字参数来覆盖任何或所有默认值,例如

>>> b = BloscArgs()               # will create a default args object
>>> b = BloscArgs(clevel=4)       # change compression level to 4
>>> b = BloscArgs(typesize=4,     # change the typesize to 4
>>> ...           clevel=9,       # change the compression level to 9
>>> ...           shuffle=False,  # deactivate the shuffle filter
>>> ...           cname='lz4')    # let lz4 be the internal codec
class BloscArgs(MutableMappingObject):
    """ Object to hold Blosc arguments.

    Parameters
    ----------
    typesize : int
        The typesize used
    clevel : int
        Compression level
    shuffle : boolean
        Whether or not to activate the shuffle filter
    cname: str
        Name of the internal code to use

    """
class BloscpackArgs(MutableMappingObject):
    """ Object to hold BloscPack arguments.

    Parameters
    ----------
    offsets : boolean
        Whether to include space for offsets
    checksum : str
        Name of the checksum to use or None/'None'
    max_app_chunks : int or callable on number of chunks
        How much space to reserve in the offsets for chunks to be appended.

    """
class MetadataArgs(MutableMappingObject):
    """ Object to hold the metadata arguments.

    Parameters
    ----------
    magic_format : 8 bytes
        Format identifier for the metadata
    meta_checksum : str
        Checksum to be used for the metadata
    meta_codec : str
        Codec to be used to compress the metadata
    meta_level : int
        Compression level for metadata
    max_meta_size : int or callable on metadata size
        How much space to reserve for additional metadata

    """

文件 / 字节

以下高级函数可用于将数据和字节对象压缩和解压缩到文件和字节对象中

  • pack_file_to_file

  • unpack_file_from_file

  • pack_bytes_to_file

  • unpack_bytes_from_file

  • pack_bytes_to_bytes

  • unpack_bytes_from_bytes

除了目标参数(如文件和字节)之外,每个 pack_* 函数还接受以下参数

chunk_size : int
    the desired chunk size in bytes
metadata : dict
    the metadata dict
blosc_args : BloscArgs
    blosc args
bloscpack_args : BloscpackArgs
    bloscpack args
metadata_args : MetadataArgs
    metadata args

以下是它们的签名

def pack_file_to_file(in_file, out_file,
                      chunk_size=DEFAULT_CHUNK_SIZE,
                      metadata=None,
                      blosc_args=None,
                      bloscpack_args=None,
                      metadata_args=None):

def unpack_file_from_file(in_file, out_file):


def pack_bytes_to_file(bytes_, out_file,
                       chunk_size=DEFAULT_CHUNK_SIZE,
                       metadata=None,
                       blosc_args=None,
                       bloscpack_args=None,
                       metadata_args=None):

def unpack_bytes_from_file(compressed_file):

def pack_bytes_to_bytes(bytes_,
                        chunk_size=DEFAULT_CHUNK_SIZE,
                        metadata=None,
                        blosc_args=None,
                        bloscpack_args=None,
                        metadata_args=None,
                        ):


def unpack_bytes_from_bytes(bytes_):

Numpy

Numpy 数组可以作为 Bloscpack 文件进行序列化,以下是一个非常简短的示例

>>> a = np.linspace(0, 1, 3e8)
>>> print a.size, a.dtype
300000000 float64
>>> bp.pack_ndarray_to_file(a, 'a.blp')
>>> b = bp.unpack_ndarray_from_file('a.blp')
>>> (a == b).all()
True

查看生成的文件,我们可以看到保存的 Numpy 元数据

$ lh a.blp
-rw------- 1 esc esc 266M Aug 13 23:21 a.blp

$ blpk info a.blp
blpk: BloscpackHeader:
blpk:     format_version: 3
blpk:     offsets: True
blpk:     metadata: True
blpk:     checksum: 'adler32'
blpk:     typesize: 8
blpk:     chunk_size: 1.0M (1048576B)
blpk:     last_chunk: 838.0K (858112B)
blpk:     nchunks: 2289
blpk:     max_app_chunks: 22890
blpk: 'offsets':
blpk: [202170,408064,554912,690452,819679,...]
blpk: 'metadata':
blpk: {   u'container': u'numpy',
blpk:     u'dtype': u'<f8',
blpk:     u'order': u'C',
blpk:     u'shape': [300000000]}
blpk: MetadataHeader:
blpk:     magic_format: 'JSON'
blpk:     meta_options: '00000000'
blpk:     meta_checksum: 'adler32'
blpk:     meta_codec: 'zlib'
blpk:     meta_level: 6
blpk:     meta_size: 67.0B (67B)
blpk:     max_meta_size: 670.0B (670B)
blpk:     meta_comp_size: 62.0B (62B)
blpk:     user_codec: ''

或者,我们也可以使用字符串作为存储

>>> a = np.linspace(0, 1, 3e8)
>>> c = pack_ndarray_to_bytes(a)
>>> b = unpack_ndarray_from_bytes(c)
>>> (a == b).all()
True

或者使用其他压缩器

>>> a = np.linspace(0, 1, 3e8)
>>> c = pack_ndarray_to_bytes(a, blosc_args=BloscArgs(cname='lz4'))
>>> b = unpack_ndarray_from_bytes(c)
>>> (a == b).all()
True
def pack_ndarray_to_file(ndarray, filename,
                         chunk_size=DEFAULT_CHUNK_SIZE,
                         blosc_args=None,
                         bloscpack_args=None,
                         metadata_args=None):

def pack_ndarray_to_bytes(ndarray,
                          chunk_size=DEFAULT_CHUNK_SIZE,
                          blosc_args=None,
                          bloscpack_args=None,
                          metadata_args=None):

def unpack_ndarray_from_file(filename):

def unpack_ndarray_from_bytes(str_):

如果您对 Bloscpack 与其他 Numpy 数组序列化格式的性能感兴趣,请参阅 EuroScipy 2013 会议论文集中 Bloscpack 论文中提供的基准测试

测试

安装依赖项

测试需要一些额外的库,您可以使用以下命令从 PyPi 安装

$ pip install -r test_requirements.txt
[...]

基本测试

基本测试,运行快速

$ nosetests
[...]

更重的测试

使用较大文件进行的扩展测试,可能需要一些时间,但会对内存很有帮助

$ nosetests test/test_file_io.py:pack_unpack_hard
[...]

使用巨大文件进行的扩展测试。这个测试要花很长时间,需要大量的内存(5G-6G)和大量的磁盘空间(10G)。使用 -s 打印进度

$ nosetests -s test/test_file_io.py:pack_unpack_extreme
[...]

请注意,某些压缩/解压缩测试会创建临时文件(在 UNIXoid 系统上位于 /tmp/blpk* 下),这些文件在相应测试完成(无论成功与否)或测试被终止(例如,使用 ctrl-c)时删除。

在罕见情况下,例如在触发删除操作时中断删除,您可能会留下污染临时空间的大型文件。根据您的分区方案等,重复这样做可能会导致您在文件系统上耗尽空间。

命令行界面测试

命令行界面使用cram进行测试

$ cram --verbose test_cmdline/*.cram
[...]

覆盖率

要确定覆盖率,可以将cram测试和单元测试的覆盖率合并在一起

$ COVERAGE=1 cram --verbose test_cmdline/*.cram
[...]
$nosetests --with-coverage --cover-package=bloscpack
[...]

测试运行器

要运行命令行界面测试、单元测试和分析覆盖率,请使用便捷的test.sh运行器

$ ./test.sh
[...]

基准

在具有2个核心和4个线程(活动超线程)的Intel(R) Core(TM) i7-3667U CPU @ 2.00GHz CPU上,使用提供的bench/blpk_vs_gzip.py脚本来测试,8GB的DDR3内存和一块Luks加密的SSD,我们得到

$ PYTHONPATH=. ./bench/blpk_vs_gzip.py
create the test data..........done

Input file size: 1.49G
Will now run bloscpack...
Time: 2.06 seconds
Output file size: 198.55M
Ratio: 7.69
Will now run gzip...
Time: 134.20 seconds
Output file size: 924.05M
Ratio: 1.65

正如之前使用python-blosc绑定对Blosc进行基准测试所预期的那样,Blosc在处理此类结构化数据时既更快,又有更好的压缩率。需要注意的是,我们并未在每一步后丢弃系统文件缓存,因此要读取的文件将缓存在内存中。为了获得更准确的图像,我们可以使用基准测试的--drop-caches开关,但这要求您以root身份运行基准测试,因为丢弃缓存需要root权限

$ PYTHONPATH=. ./bench/blpk_vs_gzip.py --drop-caches
will drop caches
create the test data..........done

Input file size: 1.49G
Will now run bloscpack...
Time: 13.49 seconds
Output file size: 198.55M
Ratio: 7.69
Will now run gzip...
Time: 137.49 seconds
Output file size: 924.05M
Ratio: 1.65

优化块大小

您可以使用提供的bench/compression_time_vs_chunk_size.py文件来优化给定机器的块大小。例如

$ sudo env PATH=$PATH PYTHONPATH=.  bench/compression_time_vs_chunk_size.py
create the test data..........done
chunk_size    comp-time       decomp-time      ratio
512.0K        8.106235        10.243908        7.679094
724.08K       4.424007        12.284307        7.092846
1.0M          6.243544        11.978932        7.685173
1.41M         4.715511        10.780901        7.596981
2.0M          4.548568        10.676304        7.688216
2.83M         4.851359        11.668394        7.572480
4.0M          4.557665        10.127647        7.689736
5.66M         4.589349        9.579627         7.667467
8.0M          5.290080        10.525652        7.690499

运行脚本需要超级用户权限,因为您需要同步磁盘写入并丢弃文件系统缓存以获得更少噪音的结果。此外,您可能需要运行此脚本几次并检查结果的可变性。

Bloscpack 格式

由于以下原因,输入被分割成块:a) 我们希望减少对主内存的压力;b) 因为Blosc有一个2GB的缓冲区限制(版本1.0.0及以上)。默认块大小为适中的1MB,即使对于不太强大的机器也应该足够。

除了块之外,还必须将一些附加信息添加到文件中以进行维护

header:

一个包含各种信息的32位头

meta:

一个可变长度的元数据部分,可能包含用户数据

offsets:

一个包含块偏移的可变长度部分

chunk:

blosc块

checksum:

如果需要,每个块后面跟一个校验和

然后文件布局如下

|-header-|-meta-|-offsets-|-chunk-|-checksum-|-chunk-|-checksum-|...|

头描述

以下32位头自版本0.3.0以来用于Bloscpack。头格式的设计目标是包含尽可能多的信息,以便在未来实现有趣的事情,并尽可能通用,以便Blaze/BLZ的持久层可以在不修改头格式的情况下实现。

以下ASCII表示显示了头的布局

|-0-|-1-|-2-|-3-|-4-|-5-|-6-|-7-|-8-|-9-|-A-|-B-|-C-|-D-|-E-|-F-|
| b   l   p   k | ^ | ^ | ^ | ^ |   chunk-size  |  last-chunk   |
                  |   |   |   |
      version ----+   |   |   |
      options --------+   |   |
     checksum ------------+   |
     typesize ----------------+

|-0-|-1-|-2-|-3-|-4-|-5-|-6-|-7-|-8-|-9-|-A-|-B-|-C-|-D-|-E-|-F-|
|            nchunks            |        max-app-chunks         |

前4个字节是魔数字符串 blpk。接着是4个字节,用于存储文件中激活的特性信息。然后是4个字节表示 chunk-size,再接着是4个字节表示 last-chunk-size,8个字节表示数据块的数量,即 nchunks,最后是8个字节表示可以附加到该文件中的最大数据块数量,即 max-app-chunks

实际上,将数据块数量存储为有符号8字节整数,将数据块数量限制为 2**63-1 = 9223372036854775807,但在实际应用中,这不应该有任何影响,因为,即使在 chunk-size 的默认值 1MB 适中时,我们仍然可以存储高达 8ZB (!)的文件。考虑到到2012年,Zettabye文件系统(zfs)中单个文件的最大大小为 16EB,Bloscpack 应该在未来几年内都是安全的。

头部条目的描述

所有条目都是小端。

版本:

(uint8) Bloscpack头部的格式版本,以确保在向前不兼容的情况下引发异常。

选项:

(bitfield) 一个位字段,允许设置该文件中的某些选项。

位0 (0x01):

如果该文件中有数据块偏移量。

位1 (0x02):

如果该文件中有元数据。

checksum:

(uint8) 所使用的校验和。以下在python标准库中可用的校验和应该得到支持。校验和始终在压缩数据上计算,并放置在数据块之后。

0:

无校验和

1:

zlib.adler32

2:

zlib.crc32

3:

hashlib.md5

4:

hashlib.sha1

5:

hashlib.sha224

6:

hashlib.sha256

7:

hashlib.sha384

8:

hashlib.sha512

typesize:

(uint8) 数据块中数据的类型大小。目前,假设类型大小是统一的。分配的空间与Blosc头部相同。

chunk-size:

(int32) 表示chunk-size。由于Blosc的最大缓冲区大小为2GB,所以有符号32位整数就足够了(2GB = 2**31 bytes)。特殊值 -1 表示chunk-size未知或可能是不均匀的。

last-chunk:

(int32) 表示最后一个数据块的大小。与chunk-size一样,int32足够了。同样,-1 表示该值未知。

nchunks:

(int64) 文件中使用的总数据块数量。给定一个字节大小的chunk-size,总数据块数量为 2**63。这相当于最大文件大小为8EB(8EB = 2*63 bytes),应该足够未来几年使用。同样,-1 表示该数量未知。

max-app-chunks:

(int64) 可以附加到该文件上的最大块数(不包括 nchunks)。只有在存在偏移量部分并且 nchunks 已知(不是 -1)的情况下才有效,如果这两个条件中任何一个不适用,则应设置为 0

整体文件大小可以通过以下公式计算:chunk-size * (nchunks - 1) + last-chunk-size。在流式场景中,可以使用 -1 作为占位符。例如,如果在创建头部时不知道总块数或最后一个块的大小。

头部条目存在以下约束

  • last-chunk 必须小于或等于 chunk-size

  • nchunks + max_app_chunks 必须小于或等于 int64 的最大值。

元数据部分的描述

本部分位于头部之后。它由元数据部分头部、序列化并可能压缩的数据部分以及可能随后的预留空间来调整数据部分大小,可能还跟随校验和。

因此,该部分的布局是

|-metadata-header-|-data-|-prealloc-|-checksum-|

头部具有以下布局

|-0-|-1-|-2-|-3-|-4-|-5-|-6-|-7-|-8-|-9-|-A-|-B-|-C-|-D-|-E-|-F-|
|         magic-format          | ^ | ^ | ^ | ^ |   meta-size   |
                                  |   |   |   |
              meta-options -------+   |   |   |
              meta-checksum ----------+   |   |
              meta-codec -----------------+   |
              meta-level ---------------------+

|-0-|-1-|-2-|-3-|-4-|-5-|-6-|-7-|-8-|-9-|-A-|-B-|-C-|-D-|-E-|-F-|
| max-meta-size |meta-comp-size |            user-codec         |
magic-format:

(8 字节 ASCII 字符串) 数据通常是某种二进制序列化字符串数据,例如 JSONBSONYAML 或 Protocol-Buffers。格式标识符应放置在此字段中。

meta-options:

(位域) 一个位域,允许在此元数据部分中设置某些选项。目前未使用。

meta-checksum:

用于元数据的校验和。与数据相同的校验和都可用。

meta-codec:

(unit8) 用于压缩元数据的编解码器。截至 Bloscpack 版本 0.3.0,支持以下编解码器。

0:

无编解码器

1:

zlib (DEFLATE)

meta-level:

(unit8) 编解码器使用的压缩级别。如果 codec0,即元数据未压缩,则此值也必须是 0

meta-size:

(uint32) 未压缩元数据的大小。

max-meta-size:

(uint32) 为数据部分分配的总空间。

meta-comp-size:

(uint32) 如果元数据已压缩,则此值表示元数据占用的总空间。如果数据未压缩,则此值与 meta-size 相同。从某种意义上说,这是元数据部分中实际使用的空间量。

user-codec:

预留空间以供使用额外的编解码器。例如,编解码器标识的 4 字节魔法字符串和编解码器参数的编码 4 字节。

预留用于扩大元数据部分的总空间是:max-meta-size - meta-comp-size

序列化元数据的 JSON 示例

'{"dtype": "float64", "shape": [1024], "others": []}'

如果请求压缩,但压缩后的大小大于未压缩的大小,则不会自动激活元数据的压缩。

截至Bloscpack版本 0.3.0,仅支持并使用JSON序列化器,并以四个空格字节作为标识符的字符串 JSON 进行标识。由于JSON和其他建议的序列化器都有局限性,因此只能存储Python结构的一小部分,因此在序列化某些类型的元数据之前,可能必须进行一些额外的对象处理。

偏移量条目的描述

在元数据部分之后,是可变长度的块偏移量部分。用于加速定位的块偏移量应指向文件中的块。偏移量(如果启用)位于头部之后。每个偏移量是一个64位有符号小端整数(int64)。值为 -1 表示未知偏移量。最初,所有偏移量都应初始化为 -1 并在写入所有块后填充。因此,如果文件压缩提前失败或被中止,所有偏移量都应具有值 -1。此外,为允许文件增长而预留的任何未使用的偏移量条目也应设置为 -1。每个偏移量表示块在文件中的确切位置,这样定位到偏移量,文件指针将定位到下一个16个字节给出Blosc头部,该头部位于所需块的开头。

块格式的描述

如前所述,每个块只是一个包括头部的Blosc压缩字符串。Blosc头部(截至 v1.0.0)是16字节,如下所示

|-0-|-1-|-2-|-3-|-4-|-5-|-6-|-7-|-8-|-9-|-A-|-B-|-C-|-D-|-E-|-F-|
  ^   ^   ^   ^ |     nbytes    |   blocksize   |    ctbytes    |
  |   |   |   |
  |   |   |   +--typesize
  |   |   +------flags
  |   +----------versionlz
  +--------------version

前四个是简单的字节,最后三个是每个4字节的未签名整数(uint32)。头部始终是小端。 ctbytes 是包括头部在内的缓冲区长度,nbytes 是未压缩时的数据长度。有关Blosc头部的更详细描述,请参阅Blosc存储库的README_HEADER.rst

开销

根据使用的文件配置,可能会添加恒定或线性开销。Bloscpack头部在任何情况下都添加32字节。如果数据不可压缩,Blosc将为每个块添加16字节的头部。元数据部分显然会增加恒定开销,如果使用,校验和和偏移量都会为文件添加开销。偏移量每个块增加8字节,校验和增加一个固定常数值,该值取决于每个块的校验和。例如,对于 adler32 校验和,为32字节。

编码规范

  • Numpy rst样式doc字符串

  • README CLI示例应使用长选项

  • 测试:预期与接收到的nt.assert_equal(expected, received)

  • 调试信息:尽可能接近数据生成的地方

  • 在消息中的歧义处使用单引号 覆盖现有文件:'testfile'

  • 异常而不是退出

  • nose测试生成器参数化测试

  • 使用维基百科对压缩比的定义:http://en.wikipedia.org/wiki/Data_compression_ratio

如何优化日志记录

在内部循环中登录时必须小心。例如,考虑以下两个提交

如果有更多块,则即使不需要记录,也会执行 double_pretty_size 调用(可能很昂贵)。

考虑以下脚本, loop-bench.py

import numpy as np
import bloscpack as bp
import blosc

shuffle = True
clevel = 9
cname = 'lz4'

a = np.arange(2.5e8)

bargs = bp.args.BloscArgs(clevel=clevel, shuffle=shuffle, cname=cname)
bpargs = bp.BloscpackArgs(offsets=False, checksum='None', max_app_chunks=0)

使用 v0.7.0 进行计时

In [1]: %run loop-bench.py

In [2]: %timeit bpc = bp.pack_ndarray_str(a, blosc_args=bargs, bloscpack_args=bpargs)
1 loops, best of 3: 423 ms per loop

In [3]: %timeit bpc = bp.pack_ndarray_str(a, blosc_args=bargs, bloscpack_args=bpargs)
1 loops, best of 3: 421 ms per loop

In [4]: bpc = bp.pack_ndarray_str(a, blosc_args=bargs, bloscpack_args=bpargs)

In [5]: %timeit a3 = bp.unpack_ndarray_str(bpc)
1 loops, best of 3: 727 ms per loop

In [6]: %timeit a3 = bp.unpack_ndarray_str(bpc)
1 loops, best of 3: 725 ms per loop

然后使用包含两个优化提交的开发版本

In [1]: %run loop-bench.py

In [2]: %timeit bpc = bp.pack_ndarray_str(a, blosc_args=bargs, bloscpack_args=bpargs)
1 loops, best of 3: 357 ms per loop

In [3]: %timeit bpc = bp.pack_ndarray_str(a, blosc_args=bargs, bloscpack_args=bpargs)
1 loops, best of 3: 357 ms per loop

In [4]: bpc = bp.pack_ndarray_str(a, blosc_args=bargs, bloscpack_args=bpargs)

In [5]: %timeit a3 = bp.unpack_ndarray_str(bpc)
1 loops, best of 3: 658 ms per loop

In [6]: %timeit a3 = bp.unpack_ndarray_str(bpc)
1 loops, best of 3: 655 ms per loop

与HDF5/PyTables的比较

由于Blosc已经支持在PyTables中使用HDF5文件,有人可能会质疑为什么还要发明另一种文件格式。本节旨在区分HDF5/PyTables,并有效论证它们不是竞争对手。

  • 轻量级与重量级。Bloscpack是一种轻量级格式。格式规范可以在一天内轻松消化,依赖性很小。PyTables是一个复杂的软件,HDF5文件格式规范是一个大文档。

  • 持久性与数据库。Bloscpack旨在允许快速序列化和反序列化内存中的数据。PyTables更像是一个数据库,例如允许对数据进行复杂查询。

此外,Bloscpack还适用于两种网络使用案例(但截至目前尚未提供支持)

  1. 流式传输:由于bloscpack没有偏移量可以单次写入,因此非常适合通过网络进行流式传输,在流式传输中可以压缩发送并按流式传输的方式解压缩单个块。

  2. 通过HTTP公开文件并进行部分读取,例如当在S3中存储压缩文件时。您只需将文件存储在Web服务器上,然后使用标题信息读取和解压缩单个块。

先验技术

以下是在Bloscpack构思和初始阶段阅读的一些重要资源。

  • 包含在FastLZ中的6pack实用工具(BloscLZ从中派生出来的编解码器)是编写Blosc命令行界面的最初灵感。

  • 维基百科上关于PNG格式的文章包含有关PNG头和文件头的有趣细节。

  • XZ文件格式规范引发了一些关于编写文件格式规范和使用校验和保证数据完整性的想法和技术。尽管格式和文档本身对我来说有点过于重量级。

  • Snappy帧格式LZ4文件容器格式也被参考过,但我记不清楚它们是否以及如何引发了灵感。

  • 在某个时候还参考了zlibgzip的主页。认为gzip/gunzip的命令行界面属于不同时代,因此Bloscpack使用了git风格的子命令。

发布版本时的维护者注意事项

  1. 将版本作为环境变量设置 VERSION=vX.X.X

  2. 更新更改日志和 ANNOUNCE.rst

  3. 使用 git commit -m "$VERSION changelog and ANNOUNCE.rst" 提交

  4. bloscpack/version.py 中设置版本号

  5. 使用 git commit -m "$VERSION" 提交

  6. 使用 git tag -s -m "Bloscpack $VERSION" $VERSION 创建标签

  7. 将提交推送到 Blosc GitHub git push blosc master

  8. 将提交推送到自己的 GitHub git push esc master

  9. 将标签推送到 Blosc GitHub git push blosc $VERSION

  10. 将标签推送到自己的 GitHub git push esc $VERSION

  11. 使用 python setup.py sdist bdist_wheel 创建源分布

  12. 使用 twine upload dist/bloscpack-$VERSION* 上传到 PyPi

  13. 将版本号提升到下一个开发版本并重置 ANNOUNCE.rst

  14. 在 Blosc 列表上宣布发布

  15. 通过 Twitter 宣布发布

待办事项

文档

  • 将单一代读文件重构为 Sphinx 并发布

  • 清理并双重检查公共 API 类的文档字符串

命令行

  • 静默输出级别

  • 允许从命令行设置 ‘max_app_chunks’

  • 允许在解压缩期间将元数据保存到文件

  • 子命令 e 或 estimate 用于估计未压缩数据的大小。

  • 子命令 v 或 verify 用于验证数据完整性

  • 添加 –raw-input 和 –raw-output 开关以允许类似以下操作:cat file | blpk –raw-input –raw-output compress > file.blp

  • 建立并记录正确的退出代码

  • 记录在 Numpy 序列化期间保存的元数据

性能分析和优化

  • 使用结构体中只有一个字符串时的更快版本

  • 内存分析器,可能可以通过在压缩和解压缩期间重用缓冲区来减少内存使用

  • 基准测试不同的编解码器

  • 使用行分析器检查代码

  • 为 Numpy 数组选择不同的默认值,没有偏移?没有预分配?

库功能

  • 可能提供类似于 GzipFile 的 BloscPackFile 抽象

  • 允许不预分配元数据的额外空间

  • 将操作数据的某些函数集合重构为对象

    • 偏移量(可能)

  • 部分解压缩?

  • 由于我们现在可能有小块数据,进度条再次变得相关

  • 配置文件以在给定机器上存储常用选项

  • 打印压缩时间,无论是详细还是调试

  • 研究是否可以使用在读取时返回内存视图的 StringIO 对象。

  • 实现一个内存视图 Compressed/PlainSource

  • 使用 bytearray 从文件中读取块。然后在每次读取时重用该 bytearray 以避免在整个过程中分配和释放字符串。

  • 许多函数的关键字参数是全球字典,这是一个坏主意,使用 forzendict 使其不可变。

  • 检查是否真的对所有 PlainSinks 进行了校验和检查

  • 一系列 NetworkSource/Sinks

  • HTTPSource/Sink

杂项

  • 在 scipy/numpy 列表、comp.compression、freshmeat、ohloh 上宣布

打包和基础设施

  • Debian 软件包(用于 python-blosc 和 bloscpack)

  • Conda 食谱(用于 python-blosc 和 bloscpack)

  • 使用 tox 测试多个 Python 版本

  • 在 travis 和 drone.io 上使用预编译构建

变更日志

  • v0.16.0 - 周四 27 12 月 2018

    • 更新 Python API 和文档

    • 各种小修复

  • v0.15.0 - 周三 31 10 月 2018

    • 万圣节发布!

    • 添加 Blosc 行为准则 (#79 by @esc)

    • 添加两个新的高级函数:’pack_bytes_to_bytes’ 和 ‘unpack_bytes_from_bytes’ (#83 by @esc)

    • 修复类型大小-块大小不匹配的检查错误 (#81 by @esc)

    • 修复测试以避免乱序追加 (#82 by @esc)

    • 修复测试以处理默认情况下不可用的 snappy (#85 by @esc)

    • 修复测试以考虑新的默认块大小 (#86 by @esc)

    • 通过 Travis 启用 Python 3.7 的测试 (#84 by @esc)

  • v0.14.0 - 周四 18 10 月 2018

    • 移除对Python 2.6的官方支持 (#77 by @esc)

  • v0.13.0 - 周四 2018年5月24日

    • 添加许可文件并将其包含在sdist软件包中 (#75 by @toddrme2178)

    • 在info中打印编解码器 (#73 by @esc)

    • 解码Blosc标志 (#72 by @esc)

    • 修复一个令人尴尬的错别字 (#71 by @esc)

    • 测试zstd (#70 by @esc)

    • 记录args对象 (#69 by @esc)

    • @esc进行的各种pep8修复

    • @esc支持上传wheel和使用twine

    • @esc修复了覆盖率的用法

    • @esc提供了更好的Python 2.6支持

  • v0.12.0 - 周五 2018年3月9日

    • 允许Pythonic None作为校验和 (#60 by @esc)

    • 修复失败的测试以符合最新的Blosc (#63和#64 by FrancescElies)

    • @esc支持通过Travis使用Python 3.6进行测试

    • @esc取消对conda配方中Blosc的锁定(谁在使用它?) (#61 by @esc)

    • 清理README (#66 by @esc)

    • 修复Trove分类器 (#67 by @esc)

    • @esc进行的随机pep8修复

  • v0.11.0 - 周一 2016年8月22日

    • 取消对python-blosc的锁定并修复单元测试 (#51和#57由@oogali修复)

    • 改进当块大小不能被类型大小整除时的块大小计算 (#52 by FrancescAlted)

  • v0.10.0 - 周四 2015年12月10日

    • 修复压缩切片数组 (#43由@mistycheney报告)

    • 修复un/pack_bytes_file以从顶层可用

    • 修复徽章,主要来自https://img.shields.io

    • 修复travis-ci,测试Python 3.5

    • 由于与Blosc 1.2.8的冲突,通过requirements.txtsetup.py将Blosc版本锁定为1.2.7。

  • v0.9.0 - 周二 2015年8月18日

    • 使用ast.literal_eval而不是np.safe_eval,后者要快得多 (#39 @cpcloud)

    • 支持将字节打包/解包到/从文件 (#41)

  • v0.8.0 - 周日 2015年7月12日

    • Python 3.x兼容性 (#14)

  • v0.7.3 - 周六 2015年7月11日

    • 修复使用版本v0.7.1及以前创建的嵌套dtypes的numpy数组的反序列化。 (#37)

  • v0.7.2 - 周三 2015年3月25日

    • 修复对零长度数组(以及输入)的支持 (#17由@dmbelov报告)

    • typesize不能整除chunk_size时捕获 (#18由@dmbelov报告)

    • 修复对象数组的序列化 (#16由@dmbelov报告)

    • 拒绝Object数据类型数组,因为它们不能与Bloscpack一起压缩

    • 为旧版本的Numpy序列化提供向后兼容性

    • 修复测试的win32兼容性 (#27由@mindw修复)

    • 修复使用setuptools脚本和依赖项 (#28由@mindw修复)

    • 各种其他修复

  • v0.7.1 - 周日 2014年6月29日

    • 修复与设置压缩Numpy数组时的正确类型大小相关的错误 (#46 by @mindw)

    • 优化内部循环中的调试语句

  • v0.7.0 - 周三 2014年5月28日

    • 模块化cram测试,甚至有点类似于一个测试套件

    • 重构、调整和简化了源/Sink代码和语义

    • 各种文档改进:列出现有技术,与HDF5的比较

    • 改进基准测试脚本

    • 引入BloscArgs对象以更合理地处理BloscArgs

    • 引入BloscpackArgs对象以更合理地处理BloscpackArgs

    • 还引入了MetadataHeader和MetdataArgs对象

    • 修复所有(希望)不正确的“压缩比率”术语的使用

    • 各种其他修复和改进

  • v0.6.0 - 周五 2014年3月28日

    • 将Bloscpack代码库重构为支持模块化

    • 支持drone.io CI服务

    • 改进Python 2.6的依赖项指定

    • 改进安装说明

  • v0.5.2 - 周五 2014年3月7日

    • 修复setup.py中的项目URL

  • v0.5.1 - 周六 2014年2月22日

    • 文档修复和改进

  • v0.5.0 - 周日 2014年2月2日

  • v0.5.0-rc1 - 周四 2014年1月30日

    • 支持Blosc 1.3.x(替代编解码器)

  • v0.4.1 - 2013年9月27日星期五

    • 修复了pack_unpack_hard测试套件

    • 修复了处理Numpy记录数组和嵌套记录数组的问题

  • v0.4.0 - 2013年9月15日星期日

    • 修复了将Numpy数组序列化为字符串时的错误

  • v0.4.0-rc2 - 2013年9月3日星期二

    • 通过PyPi(自0.4.0-rc1版起)提供软件包

    • 支持将Numpy数组打包/解包到字符串

    • 检查字符串和记录数组是否正常工作

    • 修复了PyPi软件包的安装问题(感谢Olivier Grisel)

  • v0.4.0-rc1 - 2013年8月18日星期日

    • 引入了BloscpackHeader类

    • info子命令在打印头信息时显示可读的大小

    • 现在使用Travis-CI进行测试,使用Coveralls进行覆盖率检查

    • 进一步改进Plain/Compressed-Source/Sink抽象

    • 开始在适当的地方使用memoryview

    • 学会了序列化Numpy数组

  • v0.3.0 - 2013年8月4日星期日

    • 修复了readme文件中的小错误

    • 增加cram测试的数量

  • v0.3.0-rc1 - 2013年8月1日星期四

    • Bloscpack格式更改(格式版本3)

      • 具有自身头的可变长度元数据部分

      • 能够预先分配偏移量以追加数据(max_app_chunks

    • 重构压缩和解压缩,使用文件指针而不是文件名字符串,允许使用StringIO/cStringIO。

    • 清理nchunks和chunk-size的计算

    • CLI中用于chunk-size的特殊关键字max

    • 支持向文件追加并使用append子命令(包括预先分配偏移量的能力)

    • 支持基本的info子命令

    • 使用cram添加对命令行界面的测试

    • 如往常一样进行了一些小的错误修复和更正

  • v0.2.1 - 2012年11月26日星期一

    • 向下兼容Python 2.6

    • 修复了文档中的错别字

  • v0.2.0 - 2012年9月21日星期五

    • 使用atexit魔法在终止时删除测试数据

    • 将临时目录的前缀更改为/tmp/blpk*

    • 将header RFC合并到monolithic readme

  • v0.2.0-rc2 - 2012年9月18日星期二

    • 如果文件小于默认块大小,则不会退出

    • 将默认typesize设置为8字节

    • 升级依赖项到python-blosc v1.0.5并修复测试

    • 使极端测试的资源消耗更少

    • 进行了小的错误修复和更正

  • v0.2.0-rc1 - 2012年9月13日星期四

    • 实现了RFC中描述的新头格式

    • 实现了使用各种校验和的压缩块的校验和计算

    • 实现了块在文件中的偏移量

    • 努力使库可重入,更好地控制副作用

    • README现在是rst格式而不是md(与sphinx调情)

    • 大量微小的修复、错别字、措辞、重构、重命名、pep8等。

  • v0.1.1 - 2012年7月15日星期日

    • 修复了测试中的内存问题

    • 新增两个套件:hardextreme

    • 进行了小的错别字修复和更正

  • v0.1.0 - 2012年6月14日星期四

    • 冻结头部的第一8个字节(希望永远如此)

    • 如果格式版本不匹配,则无法解压缩

    • 进行了小的错别字修复和更正

  • v0.1.0-rc3 - 2012年6月12日星期二

    • 将块大小基准测试的范围限制得更窄

    • 经过更仔细的实验后,认为默认块大小为1MB最为合适

    • 修复了一个严重的错误,在测试和基准测试期间,临时文件没有被删除,哎呀…

    • 将头部调整为有更多空间存储块,包括特殊标记用于未知块编号(-1)和压缩文件的格式版本

    • 在README中添加了有关文件格式不稳定性的说明

    • 进行了各种小的修复和增强

  • v0.1.0-rc2 - 2012年6月9日星期六

    • 默认块大小现在为 4MB

    • 可读的块大小参数

    • 最后一个块现在包含剩余数据

    • 纯Python基准测试,用于与gzip比较

    • 基准测试以测量块大小的影响

    • 进行了各种小的修复和增强

  • v0.1.0-rc1 - 2012年5月27日星期日

    • 初始版本

    • 压缩/解压缩

    • 命令行参数解析器

    • README,setup.py,测试和基准测试

感谢

  • 感谢Francesc Alted最初编写Blosc,提供持续的代码审查和对Bloscpack的反馈,以及共同编写Bloscpack文件格式规范。

项目详情


下载文件

下载适合您平台文件的文件。如果您不确定要选择哪个,请了解更多关于安装软件包的信息。

源代码分布

bloscpack-0.16.0.tar.gz (99.1 kB 查看哈希值)

上传时间 源代码

构建分布

bloscpack-0.16.0-py2-none-any.whl (75.7 kB 查看哈希值)

上传时间 Python 2

由以下支持