【问题标题】:python how to compute a simple checksum as quickly as zlib.adler32python如何像zlib.adler32一样快速计算一个简单的校验和
【发布时间】:2013-01-31 09:34:26
【问题描述】:

我希望计算一个简单的校验和:只需添加所有字节的值。

我找到的最快的方法是:

checksum = sum([ord(c) for c in buf])

但是对于 13 Mb 的数据缓冲区,需要 4.4 秒:太长(在 C 中需要 0.5 秒)

如果我使用:

checksum = zlib.adler32(buf) & 0xffffffff

耗时0.8s,但结果不是我想要的。

所以我的问题是:python 2.6 中是否包含任何函数、lib 或 C 来计算简单的校验和?

提前致谢, 埃里克。

【问题讨论】:

  • try : sum(imap(ord, buf)) , imap 来自itertools
  • 你能使用 C 扩展名吗(用 Cython 写一个很简单)?您想如何使用该值?你能用模小整数计算结果吗?相关:Simple Python Challenge: Fastest Bitwise XOR on Data Buffers
  • 请注意:如果您使用求和、异或或任何类似的操作,您不会测试数组中值位置的变化,因此请注意...

标签: python sum byte checksum ord


【解决方案1】:

你可以使用sum(bytearray(buf)):

In [1]: buf = b'a'*(13*(1<<20))

In [2]: %timeit sum(ord(c) for c in buf)
1 loops, best of 3: 1.25 s per loop

In [3]: %timeit sum(imap(ord, buf))
1 loops, best of 3: 564 ms per loop

In [4]: %timeit b=bytearray(buf); sum(b)
10 loops, best of 3: 101 ms per loop

这是用Cythonsumbytes.pyx 编写的 Python 的 C 扩展文件:

from libc.limits cimport ULLONG_MAX, UCHAR_MAX

def sumbytes(bytes buf not None):
    cdef:
        unsigned long long total = 0
        unsigned char c
    if len(buf) > (ULLONG_MAX // <size_t>UCHAR_MAX):
        raise NotImplementedError #todo: implement for > 8 PiB available memory
    for c in buf:
        total += c
    return total

sumbytesbytearray 变体快约 10 倍:

name                    time ratio
sumbytes_sumbytes    12 msec  1.00 
sumbytes_numpy     29.6 msec  2.48 
sumbytes_bytearray  122 msec 10.19 

要重现时间测量,请下载 reporttime.py 并运行:

#!/usr/bin/env python
# compile on-the-fly
import pyximport; pyximport.install() # pip install cython
import numpy as np 
from reporttime import get_functions_with_prefix, measure    
from sumbytes import sumbytes # from sumbytes.pyx

def sumbytes_sumbytes(input):
    return sumbytes(input)

def sumbytes_bytearray(input):
    return sum(bytearray(input))

def sumbytes_numpy(input):
    return np.frombuffer(input, 'uint8').sum() # @root's answer

def main():
    funcs = get_functions_with_prefix('sumbytes_')
    buf = ''.join(map(unichr, range(256))).encode('latin1') * (1 << 16)
    measure(funcs, args=[buf])

main()

【讨论】:

  • @root: 基于np.frombuffer 的解决方案很棒。它不复制缓冲区,只比上面的sumbytes C 扩展慢 2.5 倍。
  • 那是邪恶的——对于 OP 来说可能太快了——他只想要 10 倍的改进:P
【解决方案2】:

使用numpy.frombuffer(buf, "uint8").sum(),它似乎比你的例子快70倍左右:

In [9]: import numpy as np

In [10]: buf = b'a'*(13*(1<<20))

In [11]: sum(bytearray(buf))
Out[11]: 1322254336

In [12]: %timeit sum(bytearray(buf))
1 loops, best of 3: 253 ms per loop

In [13]: np.frombuffer(buf, "uint8").sum()
Out[13]: 1322254336

In [14]: %timeit np.frombuffer(buf, "uint8").sum()
10 loops, best of 3: 36.7 ms per loop

In [15]: %timeit sum([ord(c) for c in buf])
1 loops, best of 3: 2.65 s per loop

【讨论】:

  • 令人惊讶的是,sum([ord(c) for c in buf])sum(ord(c) for c in buf) 快​​,其中 buf=b'a'*(13*(1&lt;&lt;20)) 即创建一个列表然后计算其总和比使用生成器更快(一次求一个值)。
  • 如果由于生成器的上下文切换,创建列表所花费的时间可以忽略不计,我认为生成器通常比列表慢。
猜你喜欢
  • 1970-01-01
  • 2017-08-13
  • 1970-01-01
  • 2014-02-01
  • 2012-10-16
  • 2010-09-12
  • 2010-12-18
  • 2021-04-05
相关资源
最近更新 更多