对 numpy 数组的每个元素进行 md5 的快速方法

问题描述 投票:0回答:3

我正在 python 2.7 中使用 numpy 的一维数组,其中包含数千个 uint64 数字。单独计算每个数字的 md5 的最快方法是什么?

在调用 md5 函数之前,每个数字都必须转换为字符串。我在很多地方读到,迭代 numpy 的数组并在纯 python 中执行操作非常慢。有什么办法可以避免吗?

python numpy hash md5
3个回答
11
投票

您可以为 OpenSSL 的

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

对于大型数组,速度大约快 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"],
)

2
投票

我绝对建议避免将

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()
,返回二进制数据,这比将其转换为十六进制字符串更快。根据您稍后计划如何使用该数据,这可能是一个好方法。


1
投票

注意! 抱歉,我错过了这个问题。下面的代码计算整个数组的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'
© www.soinside.com 2019 - 2024. All rights reserved.