我正在开发一个系统来缓存我试图通过使用 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/