2016-03-21 9 views
1

私は、NumPy配列をデータベースに書き込むためにPythonでzlibパッケージを使用して加速しようとしている行列代数の問題の中間生成物をキャッシュするシステムを開発中です後で取り出す。私はGILをリリースすることでスピードの向上とマルチスレッド化のためにCythonを利用したいと思っています。zlibを使ってCythonのNumPy配列を圧縮する

コード私はほとんど機能していますが、私はかなり困惑していることがいくつかの問題のためにあります。まず、compress2関数をステータスコード0で返すCライブラリからインポートしたにもかかわらず(つまり、圧縮に成功した)、出力バッファのセットアップに関係なく、出力は常に切り捨てられます。次に、私がしたいことをして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 

私が切り捨てられた出力を得ている理由の一部は、バイトの文字列ではなく、ASCIIまたは任意の文字列を返す.tostring()機能に関係していることをいくつかの漠然とした考えを持っています。しかし、関数に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ベクトルを圧縮しようとすると、ちょうど最初の数バイトで羽目になる:

'x\x9cc`' 

の代わりに:

答えて

0

sizeof(c_weight_string)char*の大きさではなく、文字列のサイズです。正しいサイズを取得するには、len(weight_string)に置き換えてください。これは静的な配列なのでsizeof(destlen)はOKだと思います。

第2の問題は、出力文字列に少なくとも1つのヌル文字が含まれていることと、バイト配列への変換がそれらを出力ストリームの終わりとして扱うことです。これを解決するには

# 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を使用しての努力はとにかくすべてのzlibコードであり、かつ必要ないので(Cythonは、ここにあなたに多くの速度を取得することはほとんどありませんを検討するかもしれない


あなたは理解するc)

+0

OK、ありがとう。 compress関数の第4引数は、間違いなく問題の少なくとも一部でした。私はまだ私の機能にいくつかの問題があると思っています。 (これはもっと分かりやすいように、もっと多くの音符で質問を更新します。) もうひとつ注意しておきますが、これは関数の一部であり、GILのロックを解除できるようにしたいと思います。私はC圧縮部分が以前よりも簡単になると予想していたと思う。 – josephkibe

+0

私は100%確信していませんが、Pythonのzlibモジュールと 'numpy.savez_compressed'がGIL自体をリリースしていると思われます。残りの問題についてはわかりません。 – DavidW

関連する問題