首页 > 其他分享 >不同大小的缓冲区对 MD5 计算速度的影响

不同大小的缓冲区对 MD5 计算速度的影响

时间:2023-06-10 20:45:28浏览次数:50  
标签:rented read 计算速度 buffer 缓冲区 byte 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");
    }
}

img

基准测试是那条红色虚线,是所有测试结果中最快的。橙色的曲线是 MemoryStream 的测试结果,在缓存块的 2KB 处降到了一个较低的位置,后续耗时无明显下降。这证明 .NET 源码中使用 4KB 大小的块是一个合理的选择,但是它没有考虑文件 IO 的延迟影响。蓝色的曲线是最接*显示的测试结果,缓存块大于 32KB 时的测试结果才接*于*稳。

总结一下,MD5.HashDataAsync 过慢的原因是文件 IO 影响到了计算速度。使用文件流进行 MD5 校验的时候,缓冲区至少需要 64KB,总体速度才不会被文件 IO 拖后腿。

标签:rented,read,计算速度,buffer,缓冲区,byte,MD5
From: https://www.cnblogs.com/scighost/p/17471908.html

相关文章

  • Python使用RSA+MD5实现数字签名
    数字签名主要有防抵赖和防篡改两种功能:一是能确定消息确实是由发送方签名并发出来的,因为别人假冒不了发送方的签名,二是能确定消息的完整性。作为具体实现,发送报文时,发送方用一个哈希函数(例如MD5、SHA-1、SHA-256、SHA-384或SHA-512)从报文文本中生成报文摘要,然后用自己的私钥(由RSA......
  • 使用MD5验证传输文件的完整性和一致性
    在发布更新的时候验证资源完整性是非常重要的,这里我区分为两种,一种是更新资源的完整性,另外一种是远程执行发布任务脚本的一致性,在开发前期如果不注重这个问题的话,很可能在上线前期就会出现一些灾难性问题。第一种,shell中使用md5sum验证传输文件的完整性这个我是用shell中md5sum来做......
  • Groovy 基于Groovy实现MD5加密
    groovy3.0.7代码实现实现方式1importjava.security.MessageDigest;publicclassMD5Utils{ publicfinalstaticStringMD5(Strings){ char[]hexChars=['0','1','2','3','4','5','6',�......
  • 环形缓冲区
    typedefstruct{structHORN*Buffer;//缓冲区unsignedintpw;//写地址unsignedintpr;//读地址}RingBuffer_t,*pRingBuffer_t;staticRingBuffer_tRingBuffer;/**@brief写一个字节@paramwdata:写入的一个字节数据@retval1:环形缓冲区满0:环......
  • 超级加解密转换工具——todo,编解码 md5 sha解密可以到https://www.cmd5.com/ 注意有sa
    超级加解密转换工具V2.1绿色免费版       超级加解密转换工具可以说一款万能加密解密转换工具,支持75种方式多种转换,火星最强软件!MD5、16位MD5、MD4、拼音、大小写转换、简繁转换、GBK《-》Big5、GBK简体《-》Big5、GBK《-》SJIS、火星文转换、数字到大写金额、迅雷Thunder......
  • 基于数据驱动 U-Net 模型的大气污染物扩散快速预测,提升计算速度近6000倍
    项目背景当前,常见的大气污染预测模型大多是基于物理机理构建的,比如空气质量预测模型Calpuff、AERMOD、CMAQ等。然而,这些模型运算较为复杂,对于输入数据的要求非常高,运算耗时也比较长,适合用于常规固定区域的预报。当遇到突发污染事件时,就无法有效发挥作用。针对以上问题,本项目以某......
  • MD5 - windows也可以查询某个文件的MD5码
     命令格式certutil-hashfile文件名称md5 示例MicrosoftWindows[版本10.0.22621.1702](c)MicrosoftCorporation。保留所有权利。C:\Users\Harley·Hou\Desktop\getWifiPwd_logs>certutil-hashfilewifi-log-20230530.logmd5MD5的wifi-log-20230530.log哈希......
  • hmac(md5,sha256) 魔改算法逆向
    2bebb2b85345bac93a790d1a6986b3d5经验1貌似特征码,需要在从伪代码切换到汇编模式,再点击看具体值2找出特征码,然后google再带算法,再带csource如md50x242070DBcsource3md5和sha1在transfrom4个特征相同,sha1多两个重命名经验根据上下文关系,需要点进去发现特征量,验......
  • Groovy 基于Groovy实现MD5加密
    groovy3.0.7代码实现实现方式1importjava.security.MessageDigest;publicclassMD5Utils{ publicfinalstaticStringMD5(Strings){ char[]hexChars=['0','1','2','3','4','5','6',......
  • python md5源码
    importbinasciiimportsysimportos.pathSV=[0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,0xa679438e,0x49b40......