python - 在 Cython 中使用 zlib 压缩 NumPy 数组

标签 python c numpy cython zlib

我正在开发一个系统来缓存我试图通过使用 zlib 来加速的一些矩阵代数问题的中间产品。在 Python 中打包以将 NumPy 数组写入数据库以供以后检索。我想通过发布 GIL 来利用 Cython 来提高速度和多线程能力。

我的代码几乎可以正常运行,但是有几个问题让我感到非常烦恼。首先,尽管 compress2我从 C 库导入的函数返回状态代码 0(即,压缩成功),我的输出总是被截断,无论我设置的输出缓冲区有多大。其次,当我做我想做的事并尝试压缩 NumPy 数组时,压缩函数只返回第一个或两个字节。

#include <zlib.h>
cimport numpy as np
from libc.stdio cimport printf
from libc.stdlib cimport malloc, free

cdef extern from "zlib.h":
    ctypedef unsigned char Bytef
    ctypedef unsigned long uLongf
    ctypedef long unsigned uLong

    int compress2 (Bytef *, uLongf *, Bytef *, uLong, int)


def __cache_write(np.ndarray weights):
    weight_string = weights.tostring()
    cdef char* c_weight_string = weight_string
    cdef char compressed[1000]
    cdef uLongf destlen = sizeof(compressed)
    cdef int status = compress2(<Bytef *>compressed, &destlen, <Bytef *>c_weight_string, sizeof(c_weight_string), 6)
    cdef bytes result = compressed

    return status, result

我确实有一些模糊的想法,我得到截断输出的部分原因与 .tostring() 有关。函数返回字节串而不是 ASCII 或其他格式的字符串。但是当我让函数接受 Python 字符串(例如 'abc' )时,我也会得到截断的输出。

In [5]: __cache_write(np.ones(10))
Out[5]: (0, 'x\x9cKLJf')
...
In [7]: zlib.compress(np.ones(10).tostring())
Out[7]: 'x\x9cc`\x00\x81\x0f\xf6\x0cT\xa2\x01\xbf\xad\x0b\xd7'

这不是我的专业领域,所以对于任何新手的错误,我深表歉意!

更新 1:

正如第一个回答者所指出的,我搞砸了对 compress2 的调用功能。换出 sizeof(c_weight_string)对于 len(weight_string)确实产生了看起来更好的东西,尽管它没有解决所有问题。

当我将输入参数切换为字符串并尝试使用 'abc' 之类的东西时, 我得到 "x\x9cKLJ\x06\x00\x02M\x01'"调用 Python zlib包,但是 'x\x9cKLJf\xc0\t\xe4\xfb7\xb6\xd430<\x10\x87\xd06h\xd2\x8cP\xfa\x83\x1cD^\x01\xa4\x0e'从我的功能。 (这可能与正在压缩的内容有关,但当我尝试解压缩它时,我也遇到了缺少终止字符的错误,这表明发生了其他事情。)当我尝试压缩 NumPy vector 时,我结束只是前几个字节:

'x\x9cc`'

代替:

'x\x9cc`\x00\x81\x0f\xf6\x0cT\xa2\x01\xbf\xad\x0b\xd7'.

最佳答案

sizeof(c_weight_string)char* 的大小,而不是字符串的大小。将其替换为 len(weight_string) 以获得正确的大小。我相信 sizeof(deSTLen) 没问题,因为这是一个静态数组。

第二个问题是输出字符串包含至少一个空字符,并且转换为字节数组会将这些字符视为输出流的末尾。要解决这个问题,您可以这样做

# at top of file
from cpython cimport PyBytes_FromStringAndSize

# then in your function
cdef int status = compress2(<Bytef *>compressed, &destlen, <Bytef *>c_weight_string, len(weight_string), 6)
# generate result while passing the length too
cdef bytes result = PyBytes_FromStringAndSize(compressed,destlen)

这是一个 Python3 解决方案。对于 Python 2,您必须调用的函数可能略有不同。


您还可以考虑保存为压缩二进制格式的 numpy.savez_compressed,或者使用 Python zlib module直接(Cython 在这里不太可能给你带来很多速度,因为无论如何,工作都在 zlib 代码中,并且确实需要你理解 c)

关于python - 在 Cython 中使用 zlib 压缩 NumPy 数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36142103/

相关文章:

python - 在 numpy 数组中以不规则的时间间隔加速移动时间增量

python - 二维矩阵上的 Numpy where()

python /基维 : How to put dynamic label widget and value

Python - 在没有 if 语句的情况下将数字转换为字母

cpanel : can only access root url 中的 Python flask 应用程序路由

C 通过函数指针或不通过函数指针进行回调。为什么没有区别呢?

c - 如何用C写一个程序来测量缓存的速度?

python - 如何将深度嵌套的 JSON 文件转换为 CSV?

c - 如何使用#define 访问c 中的struct 成员

python - 获得一组线性方程的非平凡解