首页 > 解决方案 > md5 numpy 数组的快速方法

问题描述

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

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

标签: pythonnumpyhashmd5

解决方案


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

推荐阅读