使用zlib在Cython

时间:2016-03-21 21:37:52

标签: python c numpy cython zlib

我正在研究一个系统来缓存一些矩阵代数问题的中间产品我试图通过使用Python中的zlib包来将NumPy数组写入数据库以便以后加速恢复。我希望通过释放GIL来利用Cython提高速度和多线程的能力。

我的代码几乎可以运行,但是对于我发现的一些问题我觉得很烦人。首先,尽管我从C库导入的compress2函数返回状态码为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'来自我的'x\x9cc`' 功能。 (这可能与正在被压缩的内容有关,但是当我尝试解压缩它时,我也会得到一个丢失的终止字符错误,这表明正在发生其他事情。)当我尝试压缩NumPy向量时,我结束前几个字节:

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

而不是:

scalaVersion := "2.11.7"
libraryDependencies += "com.typesafe.akka" % "akka-actor_2.11" % "2.4.2"
libraryDependencies += "com.typesafe.akka" % "akka-http-experimental_2.11" % "2.4.2"
libraryDependencies += "com.typesafe.akka" % "akka-http-spray-json-experimental_2.11" % "2.4.2"
libraryDependencies += "com.typesafe.akka" % "akka-slf4j_2.11" % "2.4.2"

1 个答案:

答案 0 :(得分:0)

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

第二个问题是输出字符串包含至少一个空字符,而转换为bytearray则将它们视为输出流的结尾。要解决这个问题,你可以做到

# 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)