我正在使用Python 2.7中的NumPy 1D数组,其中包含数千个uint64数字。有什么最快的方法可以单独计算每个数字的md5值?
在调用md5函数之前,每个数字都必须转换为字符串。我在许多地方读到过,对NumPy数组进行迭代并在纯Python中执行操作非常缓慢。有没有办法绕过这个问题?
我正在使用Python 2.7中的NumPy 1D数组,其中包含数千个uint64数字。有什么最快的方法可以单独计算每个数字的md5值?
在调用md5函数之前,每个数字都必须转换为字符串。我在许多地方读到过,对NumPy数组进行迭代并在纯Python中执行操作非常缓慢。有没有办法绕过这个问题?
MD5()
函数编写一个接受NumPy数组的包装器。我们的基准将是一个纯Python实现。# build.py
import cffi
ffi = cffi.FFI()
header = r"""
void md5_array(uint64_t* buffer, int len, unsigned char* out);
"""
source = r"""
#include <stdint.h>
#include <openssl/md5.h>
void md5_array(uint64_t * buffer, int len, unsigned char * out) {
int i = 0;
for(i=0; i<len; i++) {
MD5((const unsigned char *) &buffer[i], 8, out + i*16);
}
}
"""
ffi.set_source("_md5", source, libraries=['ssl'])
ffi.cdef(header)
if __name__ == "__main__":
ffi.compile()
# md5.py
import numpy as np
import _md5
def md5_array(data):
out = np.zeros(data.shape, dtype='|S16')
_md5.lib.md5_array(
_md5.ffi.from_buffer(data),
data.size,
_md5.ffi.cast("unsigned char *", _md5.ffi.from_buffer(out))
)
return out
# run.py
import numpy as np
import hashlib
import md5
data = np.arange(16, dtype=np.uint64)
out = [hashlib.md5(i).digest() for i in data]
out2 = md5.md5_array(data)
print(data)
# [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
print(out)
# [b'}\xea6+?\xac\x8e\x00\x95jIR\xa3\xd4\xf4t', ... , b'w)\r\xf2^\x84\x11w\xbb\xa1\x94\xc1\x8c8XS']
print(out2)
# [b'}\xea6+?\xac\x8e\x00\x95jIR\xa3\xd4\xf4t', ... , b'w)\r\xf2^\x84\x11w\xbb\xa1\x94\xc1\x8c8XS']
print(all(out == out2))
# True
python build.py
python run.py
data = np.arange(100000, dtype=np.uint64)
%timeit [hashlib.md5(i).digest() for i in data]
169 ms ± 3.14 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit md5.md5_array(data)
12.1 ms ± 144 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
setup.py
文件中:setup(
...,
setup_requires=["cffi>=1.0.0"],
cffi_modules=["package/build.py:ffi"],
install_requires=["cffi>=1.0.0"],
)
cffi
版本0.14.5
(如果我没记错的话是在此发布之后),所以我认为这不是一个旧版本的问题...有人成功解决了“没有lib”问题吗?
干杯 :) - Luca Clissa_md5.c
,_md5.o
和_md5.cpython-39-x86_64-linux-gnu.so
)。这就是您所说的构建绑定吗? - Luca Clissabuild.py
后由 CFF 输出的。 - Nils Werner我强烈建议避免将uint64
转换为字符串。您可以使用struct
获取二进制数据,然后将其馈送到hashlib.md5()
中:
>>> import struct, hashlib
>>> a = struct.pack( '<Q', 0x423423423423 )
>>> a
'#4B#4B\x00\x00'
>>> hashlib.md5( a ).hexdigest()
'de0fc624a1b287881eee581ed83500d1'
>>>
这肯定会加速处理过程,因为没有转换,只是简单的字节复制。
此外,可以使用digest()
替换hexdigest()
,它返回二进制数据,比将其转换为十六进制字符串更快。根据您之后计划如何使用这些数据,这可能是一个好方法。
注意!抱歉,我错过了问题。下面的代码计算整个数组的MD5,没有任何转换。这放在了错误的位置。
>>> import hashlib
>>> import numpy as np
>>> arr = np.array([1, 2, 3, 4, 5], dtype="uint64")
>>> m = hashlib.md5(arr.astype("uint8"))
>>> m.hexdigest()
'7cfdd07889b3295d6a550914ab35e068'