快速实现md5 numpy数组的每个元素

h6my8fg2  于 2023-10-19  发布在  其他
关注(0)|答案(3)|浏览(130)

我正在python 2.7中使用numpy的1d数组,其中包含数千个uint64数字。计算每个数字的md5的最快方法是什么?
在调用md5函数之前,必须将每个数字转换为字符串。我在很多地方读到,在numpy的数组上迭代和在纯python中做一些事情非常慢。有什么办法可以绕过它吗?

ckocjqey

ckocjqey1#

您可以为OpenSSL的MD5()函数编写一个接受NumPy数组的 Package 器。我们的基线将是一个纯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()

和 Package 器

# 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

对于大型阵列,它大约快15倍(老实说,我有点失望……)

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"],
)
idfiyjo8

idfiyjo82#

我绝对建议避免将uint64转换为字符串。您可以使用struct来获取二进制数据,然后将其馈送到hashlib.md5()

>>> import struct, hashlib
>>> a = struct.pack( '<Q', 0x423423423423 )
>>> a
'#4B#4B\x00\x00'
>>> hashlib.md5( a ).hexdigest()
'de0fc624a1b287881eee581ed83500d1'
>>>

这肯定会加快这个过程,因为没有转换,只是简单的字节复制。
此外,gettig hexdigest()可以替换为digest(),它返回二进制数据,这比将其转换为十六进制字符串更快。这可能是一个很好的方法,具体取决于您计划稍后如何使用这些数据。

x8diyxa7

x8diyxa73#

**注意!”对不起,我错过了这个问题。下面的代码计算整个数组的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'

相关问题