Python 水稻Cython编码
下面是众所周知的Rice编码(=Golomb code withPython 水稻Cython编码,python,compression,cython,Python,Compression,Cython,下面是众所周知的Rice编码(=Golomb code withM=2^k)的一个实现,它在Python中广泛用于压缩算法 不幸的是,它相当缓慢。造成这种低速的原因可能是什么?(StringIO?数据是逐字节写入的事实?) 为了加快编码速度,您建议使用什么你会用什么技巧来加快Cython的速度? import struct import StringIO def put_bit(f, b): global buff, filled buff = buff | (b <&l
M=2^k
)的一个实现,它在Python中广泛用于压缩算法
不幸的是,它相当缓慢。造成这种低速的原因可能是什么?(StringIO
?数据是逐字节写入的事实?)
为了加快编码速度,您建议使用什么你会用什么技巧来加快Cython的速度?
import struct
import StringIO
def put_bit(f, b):
global buff, filled
buff = buff | (b << (7-filled))
if (filled == 7):
f.write(struct.pack('B',buff))
buff = 0
filled = 0
else:
filled += 1
def rice_code(f, x, k):
q = x / (1 << k)
for i in range(q):
put_bit(f, 1)
put_bit(f, 0)
for i in range(k-1, -1, -1):
put_bit(f, (x >> i) & 1)
def compress(L, k):
f = StringIO.StringIO()
global buff, filled
buff = 0
filled = 0
for x in L: # encode all numbers
rice_code(f, x, k)
for i in range(8-filled): # write the last byte (if necessary pad with 1111...)
put_bit(f, 1)
return f.getvalue()
if __name__ == '__main__':
print struct.pack('BBB', 0b00010010, 0b00111001, 0b01111111) #see http://fr.wikipedia.org/wiki/Codage_de_Rice#Exemples
print compress([1,2,3,10],k = 3)
导入结构
导入StringIO
def put_位(f,b):
全局buff,填充
buff=buff |(b i)和1)
def压缩(L,k):
f=StringIO.StringIO()
全局buff,填充
buff=0
填充=0
对于L中的x:#对所有数字进行编码
rice_代码(f,x,k)
对于范围内的i(8填充):#写入最后一个字节(如有必要,用1111…)填充)
put_位(f,1)
返回f.getvalue()
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
打印结构包('BBB',0b00010010,0B001111001,0b01111111)#参见http://fr.wikipedia.org/wiki/Codage_de_Rice#Exemples
打印压缩([1,2,3,10],k=3)
PS:这个问题是否应该转移到?在构建压缩结果时,我将使用C风格的缓冲区而不是StringIO,并且我将尝试在编码循环中仅使用C风格的临时性。我还注意到,您可以预先初始化缓冲区,用设置位('1'位)填充,这将加快大商编码值的速度,因为您可以简单地跳过输出缓冲区中的这些位。我重新编写压缩函数时考虑到了这些问题,并测量了结果的速度,我的版本似乎比编码器快十倍多,但生成的代码可读性较差 以下是我的版本:
cimport cpython.string
cimport libc.stdlib
cimport libc.string
import struct
cdef int BUFFER_SIZE = 4096
def compress(L, k):
result = ''
cdef unsigned cvalue
cdef char *position
cdef int bit, nbit
cdef unsigned q, r
cdef unsigned ck = k
cdef unsigned mask = (1 << ck) - 1
cdef char *buff = <char *>libc.stdlib.malloc(BUFFER_SIZE)
if buff is NULL:
raise MemoryError
try:
# Initialize the buffer space is assumed to contain all set bits
libc.string.memset(buff, 0xFF, BUFFER_SIZE)
position = buff
bit = 7
for value in L:
cvalue = value
q = cvalue >> ck
r = cvalue & mask
# Skip ahead some number of pre-set one bits for the quotient
position += q / 8
bit -= q % 8
if bit < 0:
bit += 8
position += 1
# If we have gone off the end of the buffer, extract
# the result and reset buffer pointers
while position - buff >= BUFFER_SIZE:
block = cpython.string.PyString_FromStringAndSize(
buff, BUFFER_SIZE)
result = result + block
libc.string.memset(buff, 0xFF, BUFFER_SIZE)
position = position - BUFFER_SIZE
# Clear the final bit to indicate the end of the quotient
position[0] = position[0] ^ (1 << bit)
if bit > 0:
bit = bit - 1
else:
position += 1
bit = 7
# Check for buffer overflow
if position - buff >= BUFFER_SIZE:
block = cpython.string.PyString_FromStringAndSize(
buff, BUFFER_SIZE)
result = result + block
libc.string.memset(buff, 0xFF, BUFFER_SIZE)
position = buff
# Encode the remainder bits one by one
for nbit in xrange(k - 1, -1, -1):
position[0] = (position[0] & ~(1 << bit)) | \
(((r >> nbit) & 1) << bit)
if bit > 0:
bit = bit - 1
else:
position += 1
bit = 7
# Check for buffer overflow
if position - buff >= BUFFER_SIZE:
block = cpython.string.PyString_FromStringAndSize(
buff, BUFFER_SIZE)
result = result + block
libc.string.memset(buff, 0xFF, BUFFER_SIZE)
position = buff
# Advance if we have partially used the last byte
if bit < 7:
position = position + 1
# Extract the used portion of the buffer
block = cpython.string.PyString_FromStringAndSize(
buff, position - buff)
result = result + block
return result
finally:
libc.stdlib.free(buff)
def test():
a = struct.pack('BBB', 0b00010010, 0b00111001, 0b01111111) #see http://fr.wikipedia.org/wiki/Codage_de_Rice#Exemples
b = compress([1,2,3,10],k = 3)
assert a == b
cimport cpython.string
cimport libc.stdlib
cimport libc.string
导入结构
cdef int BUFFER_SIZE=4096
def压缩(L,k):
结果=“”
cdef无符号cvalue
cdef字符*位置
cdef int位,nbit
无符号q,r
cdef无符号ck=k
cdef无符号掩码=(1>ck
r=C值和掩码
#提前跳过一些预先设置的商的1位
位置+=q/8
位-=q%8
如果位<0:
位+=8
位置+=1
#如果我们离开了缓冲区的末尾,请提取
#结果和重置缓冲区指针
当位置-buff>=缓冲区大小时:
block=cpython.string.PyString\u FromStringAndSize(
缓冲区(缓冲区大小)
结果=结果+块
libc.string.memset(buff,0xFF,缓冲区大小)
位置=位置-缓冲区大小
#清除最后一位以指示商的结束
位置[0]=位置[0]^(10:
位=位-1
其他:
位置+=1
位=7
#检查缓冲区溢出
如果位置-buff>=缓冲区大小:
block=cpython.string.PyString\u FromStringAndSize(
缓冲区(缓冲区大小)
结果=结果+块
libc.string.memset(buff,0xFF,缓冲区大小)
位置=浅黄色
#对剩余的位逐个进行编码
对于X范围内的nbit(k-1,-1,-1):
位置[0]=(位置[0]&~(1>nbit)和1)0:
位=位-1
其他:
位置+=1
位=7
#检查缓冲区溢出
如果位置-buff>=缓冲区大小:
block=cpython.string.PyString\u FromStringAndSize(
缓冲区(缓冲区大小)
结果=结果+块
libc.string.memset(buff,0xFF,缓冲区大小)
位置=浅黄色
#如果部分使用了最后一个字节,则前进
如果位<7:
位置=位置+1
#提取缓冲区的已用部分
block=cpython.string.PyString\u FromStringAndSize(
buff,位置-buff)
结果=结果+块
返回结果
最后:
libc.stdlib.free(buff)
def test():
a=结构包('BBB',0b00010010,0B001111001,0b01111111)#参见http://fr.wikipedia.org/wiki/Codage_de_Rice#Exemples
b=压缩([1,2,3,10],k=3)
断言a==b
我建议对其进行分析,看看瓶颈在哪里事实上是这样的。@ SurfFaFK你会做什么?你有一个小例子吗?在Python中已经有了一个<代码>配置文件< /C>模块。除了Python Wiki之外,这个Python代码几乎肯定是从这里移植过来的:FWIW,你也可以考虑使用<代码> cpython。此任务的ode>s,基本上是浅包装的malloc
s。有关一些示例,请参阅。