不同大小的缓冲区对 MD5 计算速度的影响
最*需要在计算大文件的 MD5 值时显示进度,于是我写了如下的代码:
public long Length {get; private set; }
public long Position { get; private set; }
public async Task ComputeMD5Async(string file, CancellationToken cancellationToken)
{
using var fs = File.OpenRead(file);
Length = fs.Length;
var task = MD5.HashDataAsync(fs, cancellationToken);
var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(10));
while (await timer.WaitForNextTickAsync(cancellationToken))
{
Position = fs.Position;
if (task.IsCompleted)
{
break;
}
}
}
运行的时候发现不对劲儿了,我的校验速度只能跑到 350MB/s,而别人的却能跑到 500MB/s,相同的设备怎么差距有这么大?带这个疑问我去看了看别人的源码,发现是这么写的:
protected long _progressPerFileSizeCurrent;
protected byte[] CheckHash(Stream stream, HashAlgorithm hashProvider, CancellationToken token)
{
byte[] buffer = new byte[1 << 20];
int read;
while ((read = stream.Read(buffer)) > 0)
{
token.ThrowIfCancellationRequested();
hashProvider.TransformBlock(buffer, 0, read, buffer, 0);
_progressPerFileSizeCurrent += read;
}
hashProvider.TransformFinalBlock(buffer, 0, read);
return hashProvider.Hash;
}
这里使用了 HashAlgorithm.TransformBlock 方法,它能计算输入字节数组指定区域的哈希值,并将中间结果暂时存储起来,最后再调用 HashAlgorithm.TransformFinalBlock 结束计算。上述代码中缓冲区 buffer 大小是 1MB,我敏锐地察觉到 MD5 计算速度可能与这个值有关,接着我又去翻了翻 MD5.HashDataAsync 的源码。
// System.Security.Cryptography.LiteHashProvider
private static async ValueTask<int> ProcessStreamAsync<T>(T hash, Stream source, Memory<byte> destination, CancellationToken cancellationToken) where T : ILiteHash
{
using (hash)
{
byte[] rented = CryptoPool.Rent(4096);
int maxRead = 0;
int read;
try
{
while ((read = await source.ReadAsync(rented, cancellationToken).ConfigureAwait(false)) > 0)
{
maxRead = Math.Max(maxRead, read);
hash.Append(rented.AsSpan(0, read));
}
return hash.Finalize(destination.Span);
}
finally
{
CryptoPool.Return(rented, clearSize: maxRead);
}
}
}
源码中最关键的是上面这部分,缓冲区 rented 设置为 4KB,与 1MB 相差甚远,原因有可能就在这里。
为了找到最佳的缓冲区值,我跑了一大堆 BenchMark,覆盖了从 32B 到 64MB 的范围。没什么技术含量,但工作量实在不小。测试使用 1GB 的文件,基准测试是对 1GB 大小的数组直接调用 MD5.HashData,实际的测试代码如下,分别使用内存流 MemoryStream 和文件流 FileStream 作为入参 Stream,对比无硬盘 IO 和实际读取文件的速度。
public async Task HashDataAsync(Stream stream)
{
var hash = MD5.Create();
byte[] buffer = new byte[1 << size];
int read = 0;
while ((read = await stream.ReadAsync(buffer)) != 0)
{
hash.TransformBlock(buffer, 0, read, buffer, 0);
}
hash.TransformFinalBlock(buffer, 0, read);
if (!(hash.Hash?.SequenceEqual(fileHash) ?? false))
{
throw new Exception("Compute error");
}
}

基准测试是那条红色虚线,是所有测试结果中最快的。橙色的曲线是 MemoryStream 的测试结果,在缓存块的 2KB 处降到了一个较低的位置,后续耗时无明显下降。这证明 .NET 源码中使用 4KB 大小的块是一个合理的选择,但是它没有考虑文件 IO 的延迟影响。蓝色的曲线是最接*显示的测试结果,缓存块大于 32KB 时的测试结果才接*于*稳。
总结一下,MD5.HashDataAsync 过慢的原因是文件 IO 影响到了计算速度。使用文件流进行 MD5 校验的时候,缓冲区至少需要 64KB,总体速度才不会被文件 IO 拖后腿。
不同大小的缓冲区对 MD5 计算速度的影响的更多相关文章
- Java中比较不同的MD5计算方式
在项目中经常需要使用计算文件的md5,用作一些用途,md5计算算法,通常在网络上查询时,一般给的算法是读取整个文件的字节流,然后计算文件的md5,这种方式当文件较大,且有很大并发量时,则可能导致内存打 ...
- iOS BCD码、数据流、字节和MD5计算
一.各个之间的相互转换 1.字符串转数据流NSData NSString *str = @"abc123"; NSData *dd = [str dataUsingEncoding ...
- php UTF8 转字节数组,后使用 MD5 计算摘要
Hex.encodeHexString(md5.digest);按 UTF8 转字节数组,后使用 MD5 计算摘要,得到 16 字节数组,使用 Hex 转为长度为 32 的字符串,保持小写 bin2h ...
- 大文本 通过 hadoop spark map reduce 获取 特征列 的 属性值 计算速度
大文本 通过 hadoop spark map reduce 获取 特征列 的 属性值 计算速度
- 一行代码加快pandas计算速度
一行代码加快pandas计算速度 DASK https://blog.csdn.net/sinat_38682860/article/details/84844964 https://cloud.te ...
- Tensorflow使用训练好的模型进行测试,发现计算速度越来越慢
实验时要对多个NN模型进行对比,依次加载直到第8个模型时,发现运行速度明显变慢而且电脑开始卡顿,查看内存占用90+%. 原因:使用过的NN模型还会保存在内存,继续加载一方面使新模型加载特别特别慢,另一 ...
- Cocos Creator 热更新文件MD5计算和需要注意的问题
Creator的热更新使用jsb.热更新基本按照 http://docs.cocos.com/creator/manual/zh/advanced-topics/hot-update.html?h=% ...
- (一)tensorflow-gpu2.0学习笔记之开篇(cpu和gpu计算速度比较)
摘要: 1.以动态图形式计算一个简单的加法 2.cpu和gpu计算力比较(包括如何指定cpu和gpu) 3.关于gpu版本的tensorflow安装问题,可以参考另一篇博文:https://www.c ...
- hashlib的md5计算
hashlib的md5计算 hashlib概述 涉及加密服务:Cryptographic Services 其中 hashlib是涉及 安全散列 和 消息摘要 ,提供多个不同的加密算法借口,如SHA1 ...
- .NET 的 Debug 和 Release build 对执行速度的影响
这篇文章发布于我的 github 博客:原文 在真正开始讨论之前先定义一下 Scope. 本文讨论的范围限于执行速度,内存占用什么的不在评估的范围之内. 本文不讨论算法:编译器带来的优化基本上属于底层 ...
随机推荐
- 【ACM算法竞赛日常训练】DAY5题解与分析【储物点的距离】【糖糖别胡说,我真的不是签到题目】| 前缀和 | 思维
DAY5共2题: 储物点的距离(前缀和) 糖糖别胡说,我真的不是签到题目(multiset,思维) 作者:Eriktse 简介:19岁,211计算机在读,现役ACM银牌选手力争以通俗易懂的方式讲解算法 ...
- 导致sql注入的根本原因
导致sql注入的根本原因 1.sql注入的定义 SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在 ...
- ChatGPT 与 Midjourney 强强联手,让先秦阿房宫重现辉煌!
Midjourney 是一款非常特殊的 AI 绘画聊天机器人,它并不是软件,也不用安装,而是直接搭载在 Discord 平台之上,所有的功能都是通过调用 Discord 的聊天机器人程序实现的.要想使 ...
- SMT贴片加工钢网工艺制作方法
smt贴片加工过程中,首先要进行锡膏印刷,而锡膏印刷的工作原理就是用机器刮刀将锡膏推送到钢网的孔洞中,使锡膏与pcb板的电子元器件接触,为下一步焊接做准备.钢网的作用就是与pcb板焊盘位置固定,使锡膏 ...
- 机器学习(六):回归分析——鸢尾花多变量回归、逻辑回归三分类只用numpy,sigmoid、实现RANSAC 线性拟合
[实验1 回归分析] 一. 预备知识 使用梯度下降法求解多变量回归问题 数据集 Iris 鸢尾花数据集是一个经典数据集,在统计学习和机器学习领域都经常被用作示例.数据集内包含 3 类共 150 条记录 ...
- Vue中Key值的一些问题
1. Vue里面的key是一个特殊的变量,在元素当中是不体现出来的 2. 在解析成虚拟DOM的是,如果我们没有写key值,那么这个key就类似于下标 0 , 1 , 2 , 3.... 3. 使用列表 ...
- Solidity 入门
基本语法 版本指令 所有Solidity源码都必须指明版本,用于标明Solidity编译器的版本,这是为了避免将来新的编译器破坏代码 pragma solidity ^0.4.20; // 声明版本 ...
- 华为云 OpenTiny 跨端、跨框架企业级开源组件库项目落地实践直播即将开启!
大家好,我是 Kagol,公众号:前端开源星球. "你们这个产品怎么只能在电脑上适配呀?我想在手机上看都不行,太麻烦了!!" "你们这个产品看起来太简单了,我想要@@功能 ...
- App复杂动画实现——Rive保姆级教程
作者:京东物流 沈明亮 在App开发过程中,如果想实现动画效果,可以粗略分为两种方式.一种是直接用代码编写,像平移.旋转等简单的动画效果,都可以这么干,如果稍微复杂点,就会对开发工程师的数学功底.图形 ...
- live555中ts流详细解析
live555中ts流详细解析 该文档主要是对live555源码下testProgs中testMPEG2TransportStreamer服务器端的详细分析.主要分析ts流实现的总体调用流程.(重新整 ...