18

我需要通过网络比较许多文件(有些可能很大,有些很小)。因此,我计划对每个客户端上的每个文件进行哈希处理,并仅通过网络发送哈希值。
这里的主要目标是性能。这意味着最小的网络流量。安全不是问题。
也应该有“零”冲突,因为我不想错误地认为两个不同的文件是相同的。话虽如此,我知道理论上总会有碰撞,我只是希望与他们实际相遇的机会绝对可以忽略不计。

所以我的问题是:哪个 .net 哈希函数最适合这项任务?

我正在考虑使用缓冲阅读器MD5CryptoServiceProvider(因为 CNG 可能并非在所有客户端上都可用)。

有没有办法获得比这更好的性能?(也许使用一些外部库?)

4

6 回答 6

19

我遇到了类似的情况,我需要一个 .NET 散列算法。我需要它用于速度比安全更重要的服务器响应缓存。当我到达这个线程时,我注意到关于算法选择以及 32 位与 64 位执行的性能差异的猜测。为了在这场辩论中引入一些科学知识,我创建了一些代码来实际测试一些可用的算法。我决定测试内置的 MD5、SHA1、SHA256 和 SHA512 算法。我还包括了force-net的 CRC32 实现和 DamienGKit 的CRC64实现。我使用 ~115MB 文件的结果如下:

在 32 位模式下运行

热身阶段

CRC32:296 MiB/s [9C54580A] 在 390 毫秒内。

CRC64:95 MiB/s [636BCF1455BC885A] 在 1212 毫秒内。

MD5:191 MiB/s [mm/JVFusWMKcT/P+IR4BjQ==],604 毫秒。

SHA1:165 MiB/s [WSFkGbnYte5EXb7kgp1kqbi2...] 在 699 毫秒内。

SHA256:93 MiB/s [USKMHQmfMil8/KL/ASyE6rm/...] 在 1240 毫秒内。

SHA512:47 MiB/s [Cp9cazN7WsydTPn+k4Xu359M...] 在 2464 毫秒内。

最终运行

CRC32:279 MiB/s [9C54580A] 在 414 毫秒内。

CRC64:96 MiB/s [636BCF1455BC885A] 在 1203 毫秒内。

MD5:197 MiB/s [mm/JVFusWMKcT/P+IR4BjQ==] 在 588 毫秒内。

SHA1:164 MiB/s [WSFkGbnYte5EXb7kgp1kqbi2...] 在 707 毫秒内。

SHA256:96 MiB/s [USKMHQmfMil8/KL/ASyE6rm/...] 在 1200 毫秒内。

SHA512:47 MiB/s [Cp9cazN7WsydTPn+k4Xu359M...] 在 2441 毫秒内。


在 64 位模式下运行

热身阶段

CRC32:310 MiB/s [9C54580A] 在 373 毫秒内。

CRC64:117 MiB/s [636BCF1455BC885A] 在 986 毫秒内。

MD5:198 MiB/s [mm/JVFusWMKcT/P+IR4BjQ==] 在 584 毫秒内。

SHA1:184 MiB/s [WSFkGbnYte5EXb7kgp1kqbi2...] 在 627 毫秒内。

SHA256:104 MiB/s [USKMHQmfMil8/KL/ASyE6rm/...] 在 1112 毫秒内。

SHA512:149 MiB/s [Cp9cazN7WsydTPn+k4Xu359M...] 在 778 毫秒内。

最终运行

CRC32:292 MiB/s [9C54580A] 在 396 毫秒内。

CRC64:119 MiB/s [636BCF1455BC885A] 在 975 毫秒内。

MD5:199 MiB/s [mm/JVFusWMKcT/P+IR4BjQ==] 在 582 毫秒内。

SHA1:192 MiB/s [WSFkGbnYte5EXb7kgp1kqbi2...] 在 601 毫秒内。

SHA256:106 MiB/s [USKMHQmfMil8/KL/ASyE6rm/...] 在 1091 毫秒内。

SHA512:157 MiB/s [Cp9cazN7WsydTPn+k4Xu359M...] 在 738 毫秒内。

这些结果是从运行 .NET v4.5.2 的已编译发行版 ASP.NET 项目中获得的。32 位和 64 位结果都来自同一台机器。在 Visual Studio 中,我通过 更改了模式Tools > Options > Projects and Solutions > Web Projects > Use the 64 bit version of IIS Express,同时更改Platform target了项目的 。

我们可以看到,虽然运行结果略有波动,但 CRC32(通过 force-net)是最快的,其次是微软的 MD5 和 SHA1。奇怪的是,选择 DamienGKit 的 CRC64 而不是内置的 MD5 或 SHA1 并没有性能优势。64 位执行似乎对 SHA512 有很大帮助,但对其他人的帮助不大。

为了回答 OP 的问题,内置的 MD5 或 SHA1似乎可以提供防撞和性能的最佳平衡。

我的代码如下:

Stopwatch timer = new Stopwatch();
Force.Crc32.Crc32Algorithm hasherCRC32 = new Force.Crc32.Crc32Algorithm();
System.Security.Cryptography.MD5Cng hasherMD5 = new System.Security.Cryptography.MD5Cng();
System.Security.Cryptography.SHA1Cng hasherSHA1 = new System.Security.Cryptography.SHA1Cng();
System.Security.Cryptography.SHA256Cng hasherSHA256 = new System.Security.Cryptography.SHA256Cng();
System.Security.Cryptography.SHA512Cng hasherSHA512 = new System.Security.Cryptography.SHA512Cng();
String result = "";
String rate = "";

Status.Text += "Running in " + ((IntPtr.Size == 8) ? "64" : "32") + "-bit mode.<br /><br />";

Status.Text += "Warm-up phase:<br />";

timer.Restart();
result = BitConverter.ToUInt32(hasherCRC32.ComputeHash(ImageUploader.FileBytes), 0).ToString("X8");
timer.Stop();
rate = ((double)ImageUploader.FileBytes.Length / timer.ElapsedMilliseconds / 1.024 / 1024).ToString("0");
Status.Text += "CRC32: " + rate + " MiB/s [" + result + "] in " + timer.ElapsedMilliseconds + "ms" + ".<br />";

timer.Restart();
result = DamienG.Security.Cryptography.Crc64Iso.Compute(ImageUploader.FileBytes).ToString("X16");
timer.Stop();
rate = ((double)ImageUploader.FileBytes.Length / timer.ElapsedMilliseconds / 1.024 / 1024).ToString("0");
Status.Text += "CRC64: " + rate + " MiB/s [" + result + "] in " + timer.ElapsedMilliseconds + "ms" + ".<br />";

timer.Restart();
result = Convert.ToBase64String(hasherMD5.ComputeHash(ImageUploader.FileBytes));
timer.Stop();
rate = ((double)ImageUploader.FileBytes.Length / timer.ElapsedMilliseconds / 1.024 / 1024).ToString("0");
Status.Text += "MD5: " + rate + " MiB/s [" + result + "] in " + timer.ElapsedMilliseconds + "ms" + ".<br />";

timer.Restart();
result = Convert.ToBase64String(hasherSHA1.ComputeHash(ImageUploader.FileBytes));
timer.Stop();
rate = ((double)ImageUploader.FileBytes.Length / timer.ElapsedMilliseconds / 1.024 / 1024).ToString("0");
Status.Text += "SHA1: " + rate + " MiB/s [" + result + "] in " + timer.ElapsedMilliseconds + "ms" + ".<br />";

timer.Restart();
result = Convert.ToBase64String(hasherSHA256.ComputeHash(ImageUploader.FileBytes));
timer.Stop();
rate = ((double)ImageUploader.FileBytes.Length / timer.ElapsedMilliseconds / 1.024 / 1024).ToString("0");
Status.Text += "SHA256: " + rate + " MiB/s [" + result + "] in " + timer.ElapsedMilliseconds + "ms" + ".<br />";

timer.Restart();
result = Convert.ToBase64String(hasherSHA512.ComputeHash(ImageUploader.FileBytes));
timer.Stop();
rate = ((double)ImageUploader.FileBytes.Length / timer.ElapsedMilliseconds / 1.024 / 1024).ToString("0");
Status.Text += "SHA512: " + rate + " MiB/s [" + result + "] in " + timer.ElapsedMilliseconds + "ms" + ".<br />";

Status.Text += "<br />Final run:<br />";

timer.Restart();
result = BitConverter.ToUInt32(hasherCRC32.ComputeHash(ImageUploader.FileBytes), 0).ToString("X8");
timer.Stop();
rate = ((double)ImageUploader.FileBytes.Length / timer.ElapsedMilliseconds / 1.024 / 1024).ToString("0");
Status.Text += "CRC32: " + rate + " MiB/s [" + result + "] in " + timer.ElapsedMilliseconds + "ms" + ".<br />";

timer.Restart();
result = DamienG.Security.Cryptography.Crc64Iso.Compute(ImageUploader.FileBytes).ToString("X16");
timer.Stop();
rate = ((double)ImageUploader.FileBytes.Length / timer.ElapsedMilliseconds / 1.024 / 1024).ToString("0");
Status.Text += "CRC64: " + rate + " MiB/s [" + result + "] in " + timer.ElapsedMilliseconds + "ms" + ".<br />";

timer.Restart();
result = Convert.ToBase64String(hasherMD5.ComputeHash(ImageUploader.FileBytes));
timer.Stop();
rate = ((double)ImageUploader.FileBytes.Length / timer.ElapsedMilliseconds / 1.024 / 1024).ToString("0");
Status.Text += "MD5: " + rate + " MiB/s [" + result + "] in " + timer.ElapsedMilliseconds + "ms" + ".<br />";

timer.Restart();
result = Convert.ToBase64String(hasherSHA1.ComputeHash(ImageUploader.FileBytes));
timer.Stop();
rate = ((double)ImageUploader.FileBytes.Length / timer.ElapsedMilliseconds / 1.024 / 1024).ToString("0");
Status.Text += "SHA1: " + rate + " MiB/s [" + result + "] in " + timer.ElapsedMilliseconds + "ms" + ".<br />";

timer.Restart();
result = Convert.ToBase64String(hasherSHA256.ComputeHash(ImageUploader.FileBytes));
timer.Stop();
rate = ((double)ImageUploader.FileBytes.Length / timer.ElapsedMilliseconds / 1.024 / 1024).ToString("0");
Status.Text += "SHA256: " + rate + " MiB/s [" + result + "] in " + timer.ElapsedMilliseconds + "ms" + ".<br />";

timer.Restart();
result = Convert.ToBase64String(hasherSHA512.ComputeHash(ImageUploader.FileBytes));
timer.Stop();
rate = ((double)ImageUploader.FileBytes.Length / timer.ElapsedMilliseconds / 1.024 / 1024).ToString("0");
Status.Text += "SHA512: " + rate + " MiB/s [" + result + "] in " + timer.ElapsedMilliseconds + "ms" + ".<br />";
于 2017-02-27T18:50:57.133 回答
14

这取决于您拥有的文件数量。

发生冲突的机会P(collision) = c/2^N(在完美的散列函数中),其中c是您的消息(文件)N数,是您的冲突算法中的位数。

由于现实世界的哈希函数并不完美,因此您有两个选择:优化速度和优化避免碰撞。

在第一种情况下,您将需要使用CRC32。CRC32 非常常见,但根据您拥有的文件数量,可能还不够:保证在大约 43 亿条消息(32 个有效位)处发生冲突,但实际上您可能会遇到第一次冲突大约 1000 万条消息。CRC32 的实现速度非常快(SSE 4.2 甚至有一个硬件指令)。CRC64 发生冲突的可能性要低得多,但没有被广泛使用,因此如果您想要比 CRC32 更多的冲突避免,您最好查看加密哈希函数。

如果您想在牺牲速度的同时避免冲突,您将需要加密哈希函数,其中 MD5(128 位)、SHA-1(160 位)和 SHA-2(通常是 SHA-256 或 SHA-512)是最广泛使用的并有快速的实现。可以使用非常有效的 MD5 哈希冲突查找算法,但是如果您输入随机消息,您将获得尽可能接近的结果P(collision) = c/2^128,同时仍在合理的时间内运行。

于 2012-04-09T14:46:31.733 回答
8

测试结果分析

除了 Michael 之前在此处所做的分析(请参阅上面的帖子)之外,我将进一步分析内置 PHP 哈希的性能,因为这个主题非常有趣并且有意想不到的结果。

结果并不那么明显,甚至令人惊讶。一种简单的算法——CRC32 或 CRC16,比复杂的算法——MD5 慢。似乎现代 CPU 不喜欢特定的旧算法并且执行它们非常缓慢,至少在算法没有以新方式实现时,利用现代 CPU 架构。CRC16 CCITT 算法在过去有 300 BPS 拨号调制解调器的情况下是相对快速和高效的。现在有专门为新硬件设计的现代算法,它在相同硬件上的运行速度可能比旧算法快得多,旧算法本质上不适合新硬件,即使你尝试优化它们,它们无论如何都会相对较慢。例如,

您可能会从其他加密库中看到,这些库证实了我们在 PHP 中看到的内容 - CRC32 IEEE 的整个速度几乎与 MD5 相同。这是另一个库结果的链接:https ://www.cryptopp.com/benchmarks.html

OpenSSL 显示了类似的结果。乍一看,这似乎很不合理,因为 CRC32 的算法比 MD5 的算法简单得多,但实际情况恰恰相反。

我只是想展示一下 CRC32 函数是多么简单。

这是使用下一个传入字节(Delphi)更新 CRCR32 计数器的代码:

   // Returns an updated CRC32
  function UpdateCrc32(CurByte: Byte; CurCrc: Cardinal): Cardinal; inline;
  begin
    UpdateCrc32 := Crc32Table[Byte(CurCrc xor CurByte)] xor (CurCrc shr 8);
  end;

这是组装的这段代码:

@calc_crc32:
    xor    dl,[esi]
    mov    al,dl
    shr    edx,8
    xor    edx,dword ptr [edi+eax*4]
    inc    esi
    loop   @calc_crc32

你也可以展开这段代码,这样你每字节只会得到 5 条 CPU 指令:

    xor    dl,bl
    shr    rbx,8
    mov    al,dl
    shr    edx,8
    xor    edx,dword ptr [r8+rax*4]

您只需将rbx接下来的 8 个字节数据加载到寄存器中,然后重复此代码 8 次,直到您需要将接下来的 8 个字节加载到rbx64 位寄存器。

这是计算整个字符串的 CRC32 的例程:

    function CalcCRC32(const B; Size: NativeUINT;
    const 
      InitialValue: Cardinal = CRC32_INIT): Cardinal;
    var
      C: Cardinal;
      P: PAnsiChar;
      i: NativeUINT;
    begin
      C := InitialValue;
      if Size > 0 then
      begin
        P := @B;
        for i := 0 to Size - 1 do
          C := UpdateCrc32(Byte(P[i]), C);
      end;
      Result := C;
    end;

下面是 Delphi 将它编译成机器代码的方式——不是非常优化,但非常简单——每个字节只有 11 条汇编指令,令人惊讶的是,在 Intel Core i5-6600 上,即使在循环展开。如您所见,实现 CRC32 IEEE 的所有这些指令都很简单,没有循环或比较,每个字节末尾只有一个比较。这只是编译的 Delphi 代码的调试器输出,而不是人工编写的汇编代码。

    CRC32.pas.78: begin
                                    push esi
                                    push edi
    CRC32.pas.80: if Size > 0 then
                                    test edx,edx
                                    jbe $00500601
    CRC32.pas.82: P := @B;
                                    mov edi,eax
    CRC32.pas.83: for i := 0 to Size - 1 do
                                    mov eax,edx
                                    dec eax
                                    test eax,eax
                                    jb $00500601
                                    inc eax
                                    xor esi,esi
    CRC32.pas.84: C := UpdateCrc32(Byte(P[i]), C);
                                    movzx edx,[edi+esi]
                                    xor dl,cl
                                    movzx edx,dl
                                    mov edx,[edx*4+$517dec]
                                    shr ecx,$08
                                    xor edx,ecx
                                    mov ecx,edx
                                    inc esi
    CRC32.pas.83: for i := 0 to Size - 1 do
                                    dec eax
                                    jnz $005005e6
    CRC32.pas.86: Result := C;
                                    mov eax,ecx
    CRC32.pas.87: end;
                                    pop edi
                                    pop esi
                                    ret

这是CRC32汇编代码的另一种变体,每个字节只有5个处理器命令,而不是11个,但它与上述汇编代码基本相同,只是使用不同的寄存器并避免再次出现在i5上的“循环”命令-比两条不同的指令快6600。您可以在从 C 控制台应用程序调用的 CRC32 汇编程序函数中找到整个代码

             586
            .model flat, stdcall 
            .xmm
            .data
            .code
        CRC32 proc sizeOfFile:DWORD, file:DWORD
            push    esi
            push    ecx
            push    edx

            mov esi, file
            xor edx, edx
            or  eax, -1
            mov ecx, sizeOfFile
    
        CRC32_loop:
            mov dl, byte ptr [esi]
            xor dl, al
            shr eax, 8
            xor eax, dword ptr [crc32_table + 4*edx]
            inc esi
            dec ecx
            jnz CRC32_loop
    
            not eax
    
            pop edx
            pop ecx
            pop esi
            ret
    

现在将它与 MD5 与 Peter Sawatzki 的高度优化的汇编代码进行比较:

; MD5_386.Asm   -  386 optimized helper routine for calculating
;                  MD Message-Digest values
; written 2/2/94 by
;
; Peter Sawatzki
; Buchenhof 3
; D58091 Hagen, Germany Fed Rep
;
; EMail: Peter@Sawatzki.de
; EMail: 100031.3002@compuserve.com
; WWW:   http://www.sawatzki.de
;
;
; original C Source was found in Dr. Dobbs Journal Sep 91
; MD5 algorithm from RSA Data Security, Inc.

.386
.MODEL FLAT
.CODE

R1 = ESi
R2 = EDi

FF Macro a,b,c,d,x,s,ac
; a:= ROL (a+x+ac + (b And c Or Not b And d), s) + b
  Add a, [EBp+(4*x)]
  Add a, ac
  Mov R1, b
  Not R1
  And R1, d
  Mov R2, c
  And R2, b
  Or  R1, R2
  Add a, R1
  Rol a, s
  Add a, b
EndM

GG Macro a,b,c,d,x,s,ac
; a:= ROL (a+x+ac + (b And d Or c And Not d), s) + b
  Add a, [EBp+(4*x)]
  Add a, ac
  Mov R1, d
  Not R1
  And R1, c
  Mov R2, d
  And R2, b
  Or  R1, R2
  Add a, R1
  Rol a, s
  Add a, b
EndM

HH Macro a,b,c,d,x,s,ac
; a:= ROL (a+x+ac + (b Xor c Xor d), s) + b
  Add a, [EBp+(4*x)]
  Add a, ac
  Mov R1, d
  Xor R1, c
  Xor R1, b
  Add a, R1
  Rol a, s
  Add a, b
EndM

II Macro a,b,c,d,x,s,ac
; a:= ROL (a+x+ac + (c Xor (b Or Not d)), s) + b
  Add a, [EBp+(4*x)]
  Add a, ac
  Mov R1, d
  Not R1
  Or  R1, b
  Xor R1, c
  Add a, R1
  Rol a, s
  Add a, b
EndM

Transform Proc
Public Transform
;Procedure Transform (Var Accu; Const Buf); Register;

; save registers that Delphi requires to be restored
  Push EBx
  Push ESi
  Push EDi
  Push EBp

  Mov EBp, EDx ; Buf -> EBp
  Push EAx     ; Accu -> Stack
  Mov EDx, [EAx+12]
  Mov ECx, [EAx+8]
  Mov EBx, [EAx+4]
  Mov EAx, [EAx]

  FF EAx,EBx,ECx,EDx,  0,  7, 0d76aa478h  ; 1
  FF EDx,EAx,EBx,ECx,  1, 12, 0e8c7b756h  ; 2
  FF ECx,EDx,EAx,EBx,  2, 17, 0242070dbh  ; 3
  FF EBx,ECx,EDx,EAx,  3, 22, 0c1bdceeeh  ; 4
  FF EAx,EBx,ECx,EDx,  4,  7, 0f57c0fafh  ; 5
  FF EDx,EAx,EBx,ECx,  5, 12, 04787c62ah  ; 6
  FF ECx,EDx,EAx,EBx,  6, 17, 0a8304613h  ; 7
  FF EBx,ECx,EDx,EAx,  7, 22, 0fd469501h  ; 8
  FF EAx,EBx,ECx,EDx,  8,  7, 0698098d8h  ; 9
  FF EDx,EAx,EBx,ECx,  9, 12, 08b44f7afh  ; 10
  FF ECx,EDx,EAx,EBx, 10, 17, 0ffff5bb1h  ; 11
  FF EBx,ECx,EDx,EAx, 11, 22, 0895cd7beh  ; 12
  FF EAx,EBx,ECx,EDx, 12,  7, 06b901122h  ; 13
  FF EDx,EAx,EBx,ECx, 13, 12, 0fd987193h  ; 14
  FF ECx,EDx,EAx,EBx, 14, 17, 0a679438eh  ; 15
  FF EBx,ECx,EDx,EAx, 15, 22, 049b40821h  ; 16

  GG EAx,EBx,ECx,EDx,  1,  5, 0f61e2562h  ; 17
  GG EDx,EAx,EBx,ECx,  6,  9, 0c040b340h  ; 18
  GG ECx,EDx,EAx,EBx, 11, 14, 0265e5a51h  ; 19
  GG EBx,ECx,EDx,EAx,  0, 20, 0e9b6c7aah  ; 20
  GG EAx,EBx,ECx,EDx,  5,  5, 0d62f105dh  ; 21
  GG EDx,EAx,EBx,ECx, 10,  9, 002441453h  ; 22
  GG ECx,EDx,EAx,EBx, 15, 14, 0d8a1e681h  ; 23
  GG EBx,ECx,EDx,EAx,  4, 20, 0e7d3fbc8h  ; 24
  GG EAx,EBx,ECx,EDx,  9,  5, 021e1cde6h  ; 25
  GG EDx,EAx,EBx,ECx, 14,  9, 0c33707d6h  ; 26
  GG ECx,EDx,EAx,EBx,  3, 14, 0f4d50d87h  ; 27
  GG EBx,ECx,EDx,EAx,  8, 20, 0455a14edh  ; 28
  GG EAx,EBx,ECx,EDx, 13,  5, 0a9e3e905h  ; 29
  GG EDx,EAx,EBx,ECx,  2,  9, 0fcefa3f8h  ; 30
  GG ECx,EDx,EAx,EBx,  7, 14, 0676f02d9h  ; 31
  GG EBx,ECx,EDx,EAx, 12, 20, 08d2a4c8ah  ; 32

  HH EAx,EBx,ECx,EDx,  5,  4, 0fffa3942h  ; 33
  HH EDx,EAx,EBx,ECx,  8, 11, 08771f681h  ; 34
  HH ECx,EDx,EAx,EBx, 11, 16, 06d9d6122h  ; 35
  HH EBx,ECx,EDx,EAx, 14, 23, 0fde5380ch  ; 36
  HH EAx,EBx,ECx,EDx,  1,  4, 0a4beea44h  ; 37
  HH EDx,EAx,EBx,ECx,  4, 11, 04bdecfa9h  ; 38
  HH ECx,EDx,EAx,EBx,  7, 16, 0f6bb4b60h  ; 39
  HH EBx,ECx,EDx,EAx, 10, 23, 0bebfbc70h  ; 40
  HH EAx,EBx,ECx,EDx, 13,  4, 0289b7ec6h  ; 41
  HH EDx,EAx,EBx,ECx,  0, 11, 0eaa127fah  ; 42
  HH ECx,EDx,EAx,EBx,  3, 16, 0d4ef3085h  ; 43
  HH EBx,ECx,EDx,EAx,  6, 23, 004881d05h  ; 44
  HH EAx,EBx,ECx,EDx,  9,  4, 0d9d4d039h  ; 45
  HH EDx,EAx,EBx,ECx, 12, 11, 0e6db99e5h  ; 46
  HH ECx,EDx,EAx,EBx, 15, 16, 01fa27cf8h  ; 47
  HH EBx,ECx,EDx,EAx,  2, 23, 0c4ac5665h  ; 48

  II EAx,EBx,ECx,EDx,  0,  6, 0f4292244h  ; 49
  II EDx,EAx,EBx,ECx,  7, 10, 0432aff97h  ; 50
  II ECx,EDx,EAx,EBx, 14, 15, 0ab9423a7h  ; 51
  II EBx,ECx,EDx,EAx,  5, 21, 0fc93a039h  ; 52
  II EAx,EBx,ECx,EDx, 12,  6, 0655b59c3h  ; 53
  II EDx,EAx,EBx,ECx,  3, 10, 08f0ccc92h  ; 54
  II ECx,EDx,EAx,EBx, 10, 15, 0ffeff47dh  ; 55
  II EBx,ECx,EDx,EAx,  1, 21, 085845dd1h  ; 56
  II EAx,EBx,ECx,EDx,  8,  6, 06fa87e4fh  ; 57
  II EDx,EAx,EBx,ECx, 15, 10, 0fe2ce6e0h  ; 58
  II ECx,EDx,EAx,EBx,  6, 15, 0a3014314h  ; 59
  II EBx,ECx,EDx,EAx, 13, 21, 04e0811a1h  ; 60
  II EAx,EBx,ECx,EDx,  4,  6, 0f7537e82h  ; 61
  II EDx,EAx,EBx,ECx, 11, 10, 0bd3af235h  ; 62
  II ECx,EDx,EAx,EBx,  2, 15, 02ad7d2bbh  ; 63
  II EBx,ECx,EDx,EAx,  9, 21, 0eb86d391h  ; 64

  Pop ESi            ; get Accu from stack
  Add [ESi],    EAx
  Add [ESi+4],  EBx
  Add [ESi+8],  ECx
  Add [ESi+12], EDx

; restore registers for Delphi
  Pop EBp
  Pop EDi
  Pop ESi
  Pop EBx

  Ret
  Transform EndP

  End

您可以在https://github.com/maximmasiutin/MD5_Transform-x64找到此代码的 32 位和 64 位版本

此代码在 IA-32 或 x86-64 下的性能是每字节(在 Skylake 上)数据 4.94 个 CPU 周期来计算 MD5。

上述代码一次性处理 64 字节的传入数据。它从执行准备步骤的主例程中调用:

    procedure CiphersMD5Update(var Context: TMD5Ctx; const ChkBuf; len:         UInt32);
    var
      BufPtr: ^Byte;
      Left: UInt32;
    begin
      If Context.Count[0] + UInt32(len) shl 3 < Context.Count[0] then
        Inc(Context.Count[1]);
      Inc(Context.Count[0], UInt32(len) shl 3);
      Inc(Context.Count[1], UInt32(len) shr 29);
    
      BufPtr := @ChkBuf;
      if Context.BLen > 0 then
      begin
        Left := 64 - Context.BLen;
        if Left > len then
          Left := len;
        Move(BufPtr^, Context.Buffer[Context.BLen], Left);
        Inc(Context.BLen, Left);
        Inc(BufPtr, Left);
        If Context.BLen < 64 then
          Exit;
        Transform(Context.State, @Context.Buffer);
        Context.BLen := 0;
        Dec(len, Left)
      end;
      while len >= 64 do
      begin
        Transform(Context.State, BufPtr);
        Inc(BufPtr, 64);
        Dec(len, 64)
      end;
      if len > 0 then
      begin
        Context.BLen := len;
        Move(BufPtr^, Context.Buffer[0], Context.BLen)
      end
    end;

    

如果您的处理器支持 CRC32 操作码 (SSE 4.2),您可以使用以下代码将校验和的计算速度提高 10 倍:

      function crc32csse42(crc: cardinal; buf: Pointer; len: NativeUInt): cardinal;
      asm // ecx=crc, rdx=buf, r8=len
        .NOFRAME
        mov eax,ecx
        not eax
        test r8,r8;   jz @0
        test rdx,rdx; jz @0
 @7:    test rdx,7;   jz @8 // align to 8 bytes boundary
        crc32 eax,byte ptr [rdx]
        inc rdx
        dec r8;     jz @0
        test rdx,7; jnz @7
 @8:    mov rcx,r8
        shr r8,3
        jz @2
 @1:    crc32 eax,qword ptr [rdx] // calculate CRC of 8 bytes, aligned
        dec r8
        lea rdx,rdx+8
        jnz @1
 @2:    // less than 8 bytes remaining
        and rcx,7; jz @0
        cmp rcx,4; jb @4
        crc32 eax,dword ptr [rdx] // calculate CRC of 4 bytes
        sub rcx,4
        lea rdx,rdx+4
        jz @0
@4:     // less than 4 bytes remaining
        crc32 eax,byte ptr [rdx]
        dec rcx; jz @0
        crc32 eax,byte ptr [rdx+1]
        dec rcx; jz @0
        crc32 eax,byte ptr [rdx+2]
@0:     not eax
      end;

请注意,在我的示例中,我使用了一个只有 5KB 的缓冲区,以适应处理器的缓存并排除较慢 RAM 对摘要计算速度的影响。

CRC32 算法的快速实现适用于不使用 CRC32 操作码的现代处理器,但利用了乱序执行,包括通过寄存器重命名的推测执行。这种实现的一个例子是 CRC32 Slicing-By-8。IA-32 或 x86-64 汇编代码每字节数据有 1,20 个 CPU 时钟周期(在 Skylake 上)。您可以在https://github.com/maximmasiutin/CRC32-Slicing-x64-Asm-Pas找到这样的实现

在 PHP 中,即使在版本 7 中,似乎也不支持 CRC32 的硬件加速,尽管 Intel 和 AMD 处理器自古以来就支持这些指令。英特尔从 2008 年 11 月开始支持 CRC32(Nehalem(微架构)),而 AMD 似乎从 2013 年开始支持它。

我自己的测试确认迈克尔的结果

我已经在不同的配置下测试了各种 PHP 哈希函数:(1)AMD FX-8320(2012 年发布)在 Ubuntu 下使用 PHP 5,以及(2)Intel Core i5-6600 在 2015 年在 Windows 下使用 PHP 7 发布。我也有在此 Intel Core i5-6600 上运行 OpenSSL 测试。除此之外,我还对我们在软件“The Bat!”中使用的加密程序进行了测试。用德尔福写的。虽然主要软件是用 Delphi 编写的,但我们使用的加密例程是用 Intel 处理器(32 位或 64 位)的 Assembler 或 C 语言编写的。

我发现我们的 Delphi 代码在各种散列函数和数据大小之间显示出非常大的速度差异。这与 PHP 形成对比,在某种程度上,除了极少数例外,从最简单的 CRC32 到曾经加密的强大 MD5 的所有哈希函数都具有几乎相同的直通输出速度。

因此,这是我在 AMD FX-8320、PHP5、Ubuntu 上所做的测量。我做了两个测试用例。首先,我运行 5000 次迭代来散列仅包含 5 个字节的消息。通过这个小消息大小,我打算测试各种算法的初始化/完成步骤的持续时间,以及它如何影响整体性能。对于某些算法,如 CRC32,三个实际上没有最终确定步骤——摘要总是在每个字节之后准备好。像 SHA1 或 MD5 或其他加密强大的函数具有将较大的上下文压缩为较小的最终摘要的最终步骤。其次,我运行 5000 次迭代来散列 5000 字节长的消息。两条消息都预先填充了伪随机字节(每次迭代后它们都不会重新填充;它们只在程序启动时填充一次)。

我的 PHP 哈希速度测试结果

我已经修改了您的 PHP 代码,使其现在适用于 PHP5 和 PHP7,在不同版本的 PHP 中生成随机数据的函数不同。我刚刚测量了哈希 5000 次 5 字节消息迭代和 5000 次 5000 字节消息迭代所需的时间。结果如下:

        Legend:
        (1) 5b x 5000, AMD FX-8320, PHP5
        (2) 5000b x 5000, AMD FX-8320, PHP5

PHP hash              (1)            (2)
--------         ------------   ------------
md2              0.021267 sec   2.602651 sec
md4              0.002684 sec   0.035243 sec
md5              0.002570 sec   0.055548 sec
sha1             0.003346 sec   0.106432 sec
sha224           0.004945 sec   0.210954 sec
sha256           0.004735 sec   0.238030 sec
sha384           0.005848 sec   0.144015 sec
sha512           0.006085 sec   0.142884 sec
ripemd128        0.003385 sec   0.120959 sec
ripemd160        0.004164 sec   0.174045 sec
ripemd256        0.003487 sec   0.121477 sec
ripemd320        0.004206 sec   0.177473 sec
whirlpool        0.009713 sec   0.509682 sec
tiger128,3       0.003414 sec   0.059028 sec
tiger160,3       0.004354 sec   0.059335 sec
tiger192,3       0.003379 sec   0.058891 sec
tiger128,4       0.003514 sec   0.073468 sec
tiger160,4       0.003602 sec   0.072329 sec
tiger192,4       0.003507 sec   0.071856 sec
snefru           0.022101 sec   1.190888 sec
snefru256        0.021972 sec   1.217704 sec
gost             0.013961 sec   0.653600 sec
adler32          0.001459 sec   0.038849 sec
crc32            0.001429 sec   0.068742 sec
crc32b           0.001553 sec   0.063308 sec
fnv132           0.001431 sec   0.038256 sec
fnv164           0.001586 sec   0.060622 sec
joaat            0.001569 sec   0.062947 sec
haval128,3       0.006747 sec   0.174759 sec
haval160,3       0.005810 sec   0.166154 sec
haval192,3       0.006129 sec   0.168382 sec
haval224,3       0.005918 sec   0.166792 sec
haval256,3       0.006119 sec   0.173360 sec
haval128,4       0.007364 sec   0.233829 sec
haval160,4       0.007917 sec   0.240273 sec
haval192,4       0.007676 sec   0.245864 sec
haval224,4       0.007580 sec   0.245249 sec
haval256,4       0.007442 sec   0.241091 sec
haval128,5       0.008651 sec   0.281248 sec
haval160,5       0.009304 sec   0.278619 sec
haval192,5       0.008972 sec   0.281235 sec
haval224,5       0.008917 sec   0.274923 sec
haval256,5       0.008853 sec   0.282171 sec

然后我在 Intel Core i5-6600 下运行相同的 PHP 脚本,在 Windows 10 下使用 64 位版本的 PHP7。结果如下:

        Legend:
        (1) 5b x 5000, Intel Core i5-6600, PHP7
        (2) 5000b x 5000, Intel Core i5-6600, PHP7


PHP hash           (1)            (2)
---------    ------------    ------------
md2          0.016131 sec    2.308100 sec
md4          0.001218 sec    0.040803 sec
md5          0.001284 sec    0.046208 sec
sha1         0.001499 sec    0.050259 sec
sha224       0.002683 sec    0.120510 sec
sha256       0.002297 sec    0.119602 sec
sha384       0.002792 sec    0.080670 sec
ripemd128    0.001984 sec    0.094280 sec
ripemd160    0.002514 sec    0.128295 sec
ripemd256    0.002015 sec    0.093887 sec
ripemd320    0.002748 sec    0.128955 sec
whirlpool    0.003402 sec    0.271102 sec
tiger128,3   0.001282 sec    0.038638 sec
tiger160,3   0.001305 sec    0.037155 sec
tiger192,3   0.001309 sec    0.037684 sec
tiger128,4   0.001618 sec    0.050690 sec
tiger160,4   0.001571 sec    0.049656 sec
tiger192,4   0.001711 sec    0.050682 sec
snefru       0.010949 sec    0.865108 sec
snefru256    0.011587 sec    0.867685 sec
gost         0.008968 sec    0.449647 sec
adler32      0.000588 sec    0.014345 sec
crc32        0.000609 sec    0.079202 sec
crc32b       0.000636 sec    0.074408 sec
fnv132       0.000570 sec    0.028157 sec
fnv164       0.000566 sec    0.028776 sec
joaat        0.000623 sec    0.042127 sec
haval128,3   0.002972 sec    0.084010 sec
haval160,3   0.002968 sec    0.083213 sec
haval192,3   0.002943 sec    0.082217 sec
haval224,3   0.002798 sec    0.084726 sec
haval256,3   0.002995 sec    0.082568 sec
haval128,4   0.003659 sec    0.112680 sec
haval160,4   0.003858 sec    0.111462 sec
haval192,4   0.003526 sec    0.112510 sec
haval224,4   0.003671 sec    0.111656 sec
haval256,4   0.003636 sec    0.111236 sec
haval128,5   0.004488 sec    0.140130 sec
haval160,5   0.005095 sec    0.137777 sec
haval192,5   0.004117 sec    0.140711 sec
haval224,5   0.004311 sec    0.139564 sec
haval256,5   0.004382 sec    0.138345 sec

如您所见,要在 PHP 中计算消息的 CRC32,在我几乎所有的测试中,只需要计算相同消息的 MD5 所需时间的一半左右。唯一的例外是在英特尔酷睿 i5-6600 上使用 PHP7 测试 5000 条 5000 字节的消息时,CRC32 甚至比 MD5 花费的时间更长(!)。这个奇怪的结果在我的案例中总是可以重复的。我找不到合理的解释。

此外,在 PHP 上,MD5 和 SHA1 之间几乎没有明显的速度差异,除了在带有 PHP5 的 Ubuntu 上,在测试 5000 条 5000 字节的消息时,MD5 快了两倍。

我的 OpenSSL 哈希性能测试结果

以下是英特尔 i5-660 上的 OpenSSL 测试。它们以不同的方式显示数据。它们没有显示消化某些数据集花费了多少时间,但反之亦然:它们显示了 OpenSSL 在 3 秒内设法散列的数据量。因此,值越高越好:

Legend:
 (1) OpenSSL 1.1.0 on Intel Core i5-6600, number of 16-bytes messages processed in 3 seconds
 (2) OpenSSL 1.1.0 on Intel Core i5-6600, number of 8192-bytes messages processed in 3 seconds


Algorighm              (1)            (2)
---------         ---------      ----------
md4               50390.16k      817875.48k
md5              115875.35k      680700.59k
sha1             118158.30k      995986.09k
ripemd160         30308.79k      213224.11k
whirlpool         39605.02k      182072.66k

同样,md5 和 sha1 之间几乎没有区别,这很奇怪,需要进一步调查 MD5 和 SHA-1 算法在时间消耗方面是否本质上相同。

我们的 Delphi 哈希函数性能结果

以下是我们在英特尔酷睿 i5-6600 上的 Delphi 库在 Windows 10 64 位下的结果,测试的代码是 32 位 Win32 应用程序。

      Legend:
        (1) Delphi, 5b x 5000 iterations
        (2) Delphi, 5000b x 5000 iterations

Algorighm                  (1)                     (2)
---------------      --------------         --------------
md2                  0.0381010 secs         5.8495807 secs
md5                  0.0005015 secs         0.0376252 secs
sha1                 0.0050118 secs         0.1830871 secs
crc32               >0.0000001 secs         0.0581535 secs
crc32c (intel hw)   >0.0000001 secs         0.0055349 secs

正如你所看到的,MD2 也比其他哈希值要多得多——与 PHP 代码的结果相同,但 MD5 比 SHA-1 快得多,总体而言,在同一台机器上在 Delphi 中执行相同操作所需的时间更少以 PHP 为例,PHP7 用 MD5 消化 5000 条 5 字节消息需要 0.001284 秒,用 SHA1 需要 0.001499 秒。大约 5000 字节的消息——使用 MD5 需要 PHP7 0.046208 秒,使用 SHA-1 需要 0.050259 秒。

与 Delphi 一样,使用 MD5 消化 5000 条 5 字节消息需要 0.0005015 秒,而使用 SHA1 需要 0.0050118 秒。大约 5000 字节的消息——使用 MD5 需要 Delphi 0.0376252 秒,使用 SHA-1 需要 0.1830871 秒。如您所见,MD5 在 Delphi 中运行得更快,但 SHA-1 几乎相同。此外,Delphi 在 5 字节消息上的速度大约快 10 倍,但对于大约 5000 字节的消息,它与 SHA-1 大致相同甚至更慢。

但是在 CRC32 和 CRC32C 方面,Delphi 是无与伦比的,比 PHP 快 10 到 1000 倍。

结论

PHP 在循环和小型操作上天生就很慢。因此,如果您需要计算一条小消息的哈希值,那么在 PHP 上调用哪个哈希函数并不重要。但如果你需要消化一条大消息,算法速度的差异就开始显现出来:例如,MD2 的性能大约是 MD5 的十倍。无论如何,今天绝对没有理由使用 MD2。对于使用 MD2 的 PHP 用户而言,相比 MD5 没有任何优势。至于 MD5,最初设计为加密哈希函数,然后在 RFC-1991 中被 PGP 使用,现在不能再用于密码学,但可以在可信环境中用作校验和,例如用于 ETag 或其他方式。这个函数在正确实现时非常快(并且在 PHP 上,它至少不慢),与其他函数相比,MD5 产生了非常紧凑的摘要。这是我进行这些基准测试的 PHP 代码。我是根据 Michael 的原始代码示例制作的(见上文)。

<?
 define (TRAILING_ZEROS, 6);
 $strlens = array(5, 30, 90, 1000, 5000);
 $hashes = hash_algos();

 function generate_bytes($len)
 {
   if (function_exists('random_bytes')) {$fn='random_bytes';$str = random_bytes($len);} else // for php 5
   if (function_exists('openssl_random_pseudo_bytes')) {$fn='openssl_random_pseudo_bytes';$str = openssl_random_pseudo_bytes($strlen);} else // for php 7
   {
        flush();
        ob_start () ;
        phpinfo () ;
        $str = str_pad(substr(ob_get_contents (), 0, $len), $len) ;
        ob_end_clean () ;
        $fn = 'phpinfo';
   }
   return array(0=>$str, 1=>$fn);
 }

 foreach ($strlens as $strlen)
 {

 $loops = 5000;
 echo "<h1>$loops iterations on $strlen bytes message</h1>".PHP_EOL;
 echo '<p>';
 $r = generate_bytes($strlen);
 $str = $r[0];
 $gotlen = strlen($str);
 while ($gotlen < $strlen)
 {
   // for some uncodumented reason, the  openssl_random_pseudo_bytes returned less bytes than needed
   $left = $strlen-$gotlen;
   echo "The ".$r[1]."() function returned $left byes less, trying again to get these remaining bytes only<br>";
   $r = generate_bytes($left);
   $str.= $r[0];
   $gotlen = strlen($str);
 };

 echo "Got the whole string of ".strlen($str)." bytes!";
 echo '</p>';
 echo PHP_EOL;
 echo "<pre>";

 foreach ($hashes as $hash)
 {
        $tss = microtime(true);
        for($i=0; $i<$loops; $i++)
        {
                $x = hash($hash, $str, true);
        }
        $tse = microtime(true);
        echo "\n".str_pad($hash, 15, ' ')."\t" . str_pad(round($tse-$tss, TRAILING_ZEROS), TRAILING_ZEROS+2, '0') . " sec \t" . bin2hex($x);
 }

 echo PHP_EOL."</pre>".PHP_EOL;
 flush();
 }
?>

帖子继续……

于 2017-04-18T05:42:21.947 回答
5

哈希函数不是为了速度而构建的,因此它们不是这项工作的好人选。在这种情况下,它们的优势(加密安全)也无关紧要。

您应该考虑使用 CRC 或其他校验和函数;这里有一个常用的列表。HashLib有现成的实现。

于 2012-04-09T07:39:54.593 回答
3

我相信您在这种情况下误解了哈希的目的。

这是快速“这些一样”的检查。哈希不能告诉你两件事是否相等,因为它们发生冲突。

因此,考虑到一个简单的 CRC 很少发生冲突,以及它在大量文件上的速度有多快,这是一个更好的解决方案。

如果两个哈希值或 CRC 相同,您的反应应该完全相同:通过实际内容验证相等性。您甚至可以在 CRC 匹配后散列/CRC 一个相等大小的子集 - 并检查文件大小 - 以便快速“排除”检查。


如果您希望有许多相同的文件,哈希仍然不能消除检查的需要,但它会减少需要。您仍然需要进行其他类型的检查。哈希相等,加上文件长度匹配,加上部分哈希相等(x例如,对文件的第一个字节进行哈希处理)可能就足够了,具体取决于您的需要。

于 2012-04-09T08:27:56.053 回答
1

我遇到了单个帖子大小的限制,所以我将在第二个帖子中继续。

“选择哈希函数以获得最佳性能”问题的最后说明

至于最初的问题:“选择一个哈希函数以获得最佳性能”,我的看法如下。如果您使用 PHP,请考虑使用 MD5 来计算各种哈希和摘要。与 CRC32 相比,它在速度上几乎没有差异,至少目前在 PHP 5 和 7 中是如何实现的。一般来说,如果您使用 PHP,MD5 与 PHP 中可用的其他哈希相比在性能上有显着差异,当谈到更大的消息。MD5 的优点是它生成的摘要大小相对较小,而且速度非常快。

如果您有 AES (AES-NI) 的硬件实现,则可以在 CBC 模式下使用 AES 来生成摘要。它将比 MD-5 快得多,具有相同的摘要大小(128 位)。

提示:如果您需要更小的摘要但 PHP 的文本形式,请使用 md5 的二进制输出的 base64_encode,它会产生比默认使用的十六进制编码更短的结果字符串。

从低级编程语言中使用哪个哈希函数

如果您使用的是 Delphi 或 C++ 之类的编程语言,请找到 CRC32C 的良好实现,它可以在 Intel 和 AMD 现代处理器上通过硬件加速运行。

正如您从我的 Delphi 测试结果中看到的,我们的代码计算了 5000 条消息的校验和,每条消息长度为 5000 字节,总体耗时仅为 0.0055349 秒。它比我们的 CRC32 的非硬件实现快了大约十倍,而后者又比用 PHP 实现的快得多。

如果您需要更大的摘要,而不仅仅是 CRC32 生成的 4 个字节,而是至少 16 个字节,请考虑找到一个高性能的 MD5 实现并使用 MD5 来生成您的摘要。MD5 是作为加密散列函数开发的,用于 PGP 加密和数字签名。它如何不再适合加密,但对于消息摘要来说仍然可以。这个散列函数不能再用于密码学,因为很容易发现冲突,但是如果你只需要将它用于你自己的校验和而不是冲突攻击的问题,我建议即使在 2017 年的今天也使用 MD5,前提是你已经找到了这个著名的散列函数的快速实现。如果您害怕冲突,并且您有 AES (AES-NI) 的硬件实现,那么请使用 AES-CBC 作为摘要。

具有较大摘要的哈希可能有用的实际示例

让我解释一下为什么有人可能需要一个具有更大摘要大小的哈希函数,比如 16 个字节,而乍一看,你可以使用更短的一次,比如只有 4 个字节的 CRC32?

如果更大的散列不是问题,就不会有像“选择散列函数以获得最佳性能”这样的问题(参见最初的帖子)。

假设您有一个由应用程序服务器和“memcached”服务器组成的受信任环境,其中所有应用程序都可以访问运行“memcached”恶魔的服务器上使用的所有数据,并且可以通过简单的文本键访问数据。我看到人们发明了更长的字符串,以便键在不同的领域、应用程序中是唯一的,即不重叠。因此,根据维护集群或集群组的所有程序员和管理员的惯例,他们通常同意密钥应由不同的强制段组成,如下所示: $UniqueKey = "$Namespace|$Realm|$Application| $AppComonent|$User|$Key";

这会产生非常长的键,大约 60 个字符或更多。

对于“memcached”守护进程,数据存储在固定大小记录(块)的slab中,键+值的长度构成了单个块的大小,因此slabs是96字节的小块,120字节和 192 字节几乎是空的,而真正使用的第一个平板是 304 字节块。为了避免较长密钥的这种低效率,按照惯例,您可以同意始终通过预先确定的哈希函数(如 MD5)来消化所有这些密钥。如果所有开发人员都使用相同格式的密钥,并且总是通过某个哈希函数对这些密钥进行哈希处理,那么在这个可信环境中密钥可能重叠的实际风险是不存在的。您只需对 MD 的二进制输出进行 base-64 编码并去除尾随的“=”填充,并且,可选地,

Suj5_RxNfIq4u-36o03afg
StRL3WgcNM6AjTSW4ozf8g
i4Ev9nJNFpmf928PrkWbIw
b_GE6cp9c-PT_PLwwYbDXQ
Znci1Nj3HprfFLa0cQNi5g
6ns__XWR7xlsvPgGwZJLBQ
9_Yse6hFEyzgl5y5fnZaUg
LYoIQyhNpmAHqY4r-fgZXg
Y1fVl2rBaan0sKz-qrb8lQ
CiLmDZwUVNW09fQaTv_qSg
easjBIYq27dijGr2o01-5Q

以不同的方式布置测试结果

让我还以不同的方式向您展示哈希性能测试结果:除以经过的时间,因此您可以通过输出以 KB/S 看到特定的哈希:

散列 50000000 个 64 字节的数据块

通过通用指令在汇编中实现的 CRC32 CCITT

  • 32 位:7.2012 秒,423.7874 MB/秒
  • 64 位:7.1871 秒、424.6137 MB/秒

通过通用指令在 Delphi 中实现的 CRC32 CCITT

  • 32 位:7.1350 秒、427.7164 MB/秒
  • 64 位:7.3686 秒、414.1570 MB/秒

通过通用指令在汇编中实现的 CRC32C (RFC 3720)

  • 32 位:2.4866 秒、1227.2629 MB/秒
  • 64 位:2.7694 秒、1101.9702 MB/秒

通过 SSE 4.2 CRC32C 指令实现的 CRC32C

  • 32 位:0.7099 秒、4298.7911 MB/秒
  • 64 位:0.7510 秒,4063.6096 MB/秒

通过通用指令在汇编中实现 MD5

  • 32 位:4.4489 秒、685.9647 MB​​/秒
  • 64 位:4.4369 秒、687.8157 MB/秒

AES,通过通用指令在汇编中实现

  • 32 位:23.6519 秒、129.0280 MB/秒
  • 64 位:28.1875 秒、108.2662 MB/秒

AES,通过 AES-NI 指令实现

  • 32 位:1.6374 秒、1863.8040 MB/秒
  • 64 位:1.6063 秒、1899.8995 MB/秒

我相信您已经发现哈希问题非常重要,所以它是!

于 2017-04-18T17:49:33.970 回答