# MD5 计算时间的实践

# 引言

最近在做一个项目,里面用到了 MD5 算法来生成文件名,并且可以对相同文件进行合并存储,不禁让我想到了一个问题 ——MD5 的计算很耗时吗还是 MD5 是一个轻量级的算法,在网上翻阅了一些资料,得到了结果,确实 MD5 的计算是很轻量的。

但是,他到底需要多长时间呢?我直接开始实践一下。

# 配置

机器采用:AMD Ryzen 7 5800H 处理器 + 32GB 内存 频率为 3200 使用约 50%

# 实践

先对小文件进行测试,现选用文件大小为 2.83 MB 的图片,在 anaconda 环境下,对比不同 chunk_size 下的计算时间。

import hashlib
import time
def file_to_md5(file_path, chunk_size=8192):
    md5 = hashlib.md5()
    with open(file_path, 'rb') as f:
        while chunk := f.read(chunk_size):
            md5.update(chunk)
    return md5.hexdigest()
t = time.perf_counter()
file_to_md5('D:\\ZzMu\\Aping\\bz\\zb.png')
print(f'{time.perf_counter() - t:.8f}s')
chunk_size 时间
1024 0.00551090s
2048 0.00501470s
4096 0.00491630s
8192 0.00470980s
16384 0.00436570s

随着 chunk_size 的增大,计算时间不断缩短,不过确实微乎其微,0.00 几的时间无法感知,那么大一点的文件能放大影响吗?

下面对文件大小为 957 MB 的 4 分 44 秒的 5k 视频进行计算。

chunk_size 时间
1024 1.68504020s
2048 1.61570920s
4096 1.57428070s
8192 1.55151410s
16384 1.35576710s
32768 1.28117580s

大文件对 chunk_size 的感知更为明显,变现为 0. 几的波动。

下面试试更大的文件,文件大小为 9.58 GB 的 Linux 安装文件 CentOS-7-x86_64-Everything-2207-02.iso 进行计算。

chunk_size 时间
1024 17.36447650s
2048 17.03117640s
4096 16.44976660s
8192 16.01030290s
16384 14.48009140s
32768 13.57228980s

得出结论,越大的文件对 chunk_size 越敏感。到此为止,我仍然对这个执行时间有疑问,不同的变成语言对计算时间有什么影响呢,以上是使用 Python 语言完成的测试,接下来试试 Java 的语言。

原生:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Main {
    private static final int BUFFER_SIZE = 32768;
    public static String fileToMD5(String filePath) throws IOException, NoSuchAlgorithmException {
        try (InputStream is = new FileInputStream(filePath)) {
            MessageDigest md5Digest = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                md5Digest.update(buffer, 0, bytesRead);
            }
            return bytesToHex(md5Digest.digest());
        }
    }
    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) hexString.append('0');
            hexString.append(hex);
        }
        return hexString.toString();
    }
    public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
        long startTime = System.nanoTime();
        fileToMD5("D:\\ZzMu\\Aping\\bz\\zb.png");
        long endTime = System.nanoTime();
        System.out.printf("Time taken: %.8fs%n", (endTime - startTime) / 1e9);
    }
}

使用 java 8:

chunk_size 时间
8192 22.89824750
16384 21.72088020
32768 20.98676390

没想到啊,java 表现的更为糟糕,是不是和 java 版本有关系呢?

使用 java 17:

chunk_size 时间
1024 27.40612860
8192 15.47918140
16384 14.53450360
32768 13.85291620

确实 java 17 的表现要比 java 8 的表现好。不过还是弱于 Python?难道是我写的代码太烂了?

又引入了 DigestUtils 工具类,DigestUtils 是 apache. commons 包下的的 MD5 计算工具,默认 chunk_size 大小为 1024。

import org.apache.commons.codec.digest.DigestUtils;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.IOException;
public class MD5 {
    @Test
    public void test() throws IOException {
        FileInputStream inputStream = new FileInputStream("E:\\LinuxIso\\CentOS-7-x86_64-Everything-2207-02.iso");
        long startTime = System.nanoTime();
        DigestUtils.md5Hex(inputStream);
        long endTime = System.nanoTime();
        System.out.printf("Time taken: %.8fs%n", (endTime - startTime) / 1e9);
    }
}
chunk_size 时间
1024 27.50305870

彼此彼此吧,哈哈哈哈哈哈。

下面又引入了 SecureUtil,该工具是 HuTool 包下一个计算 MD5 的工具,默认 chunk_size 为 1024。

import cn.hutool.crypto.SecureUtil;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.IOException;
public class MD5 {
    @Test
    public void test() throws IOException {
        FileInputStream inputStream = new FileInputStream("E:\\LinuxIso\\CentOS-7-x86_64-Everything-2207-02.iso");
        long startTime = System.nanoTime();
        SecureUtil.md5(inputStream);
        long endTime = System.nanoTime();
        System.out.printf("Time taken: %.8fs%n", (endTime - startTime) / 1e9);
    }
}
chunk_size 时间
1024 15.42640900

HuTool 的 SecureUtil 确实更高效,应该是做了一些特殊优化,源码中显示的是 MD5 没有使用 BC 库,完全依赖于 jdk 库。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

KarryLiu 微信支付

微信支付

KarryLiu 支付宝

支付宝