Choosing a hash function for better performance

I need to compare many files (some of them may be large and some may be small) over the network. Therefore, I plan to hash each file on each client and send only the hash value over the network.
The main goal here is performance . This implies minimal network traffic. Security is not a problem.
There should also be β€œzero” collisions , since I do not want each time to mistakenly consider two different files as the same. Saying this, I know that theoretically there are always collisions, I just want to almost always encounter them absolutely negligible.

So my question is: which .net hash function is best suited for this task?

I thought to use a buffered reader and MD5CryptoServiceProvider (since CNG may not be available to all clients).

Is there a way to improve performance? (maybe using some external library?)

+7
source share
6 answers

It depends on the number of files you have.

Collision probability P(collision) = c/2^N (in an ideal hash function), where c is your number of messages (files) and N is the number of bits in your collision algorithm.

Since the hash functions of the real world are not perfect, you have two options: speed optimization and optimization to prevent collisions.

In the first case, you will want to use CRC32 . CRC32 is very common, but depending on the number of files you have, it may not be enough: you are guaranteed a collision of ~ 4.3 billion messages (32 effective bits), but in practice you may encounter the first collision at ~ 10 million messages. CRC32 has a very fast implementation (SSE 4.2 even has hardware instructions for it). CRC64 has a much lower chance of collision, but is not widely used, so if you want to avoid collisions more than CRC32, take a look at cryptographic hash functions.

If you want to avoid collisions while reducing speed, you will need cryptographic hash functions, of which MD5 (128 bits), SHA-1 (160 bits) and SHA-2 (usually SHA-256 or SHA-512) are the most widely used and have a quick implementation. Very efficient hash collision detection algorithms are available for MD5, but if you enter random messages you will get as close to P(collision) = c/2^128 as you will ever receive while still working in a reasonable amount of time .

+6
source

Hash functions are not designed for speed, so they are not good candidates for work. Their advantage (cryptographic security) also does not matter in this scenario.

You should study the use of CRC or another checksum function; There is a list of the most commonly used here . HashLib has ready-made implementations.

+4
source

I believe that you underestimate the purpose of the hash in this case.

This means that a quick check "these are not the same." Hashes cannot tell you if two things are equal because they will collide.

So, given how rare a simple CRC has come across, and how much faster it will be on a large number of files, this is the best solution.

If the two hashes or CRCs are the same, your reaction should be exactly the same: check for equality against the actual contents. You could even then use a hash / CRC subset of equal size - and check the file sizes - for quick "rule it out" checks after matching CRC.


If you expect to have many identical files, the hash still does not eliminate the need for verification otherwise, but it will reduce the need. You will still want to do some other check. Hash uniformity, plus file length matching, plus partial hash equality (e.g. hashing the first x bytes of a file) can be quite good, depending on your needs.

+3
source

I was in a similar situation when I needed a .NET hash algorithm. I needed this to cache server responses where speed was more important than security. When I got to this topic, I noticed speculation about the differences in performance in the choice of algorithm and in 32-bit and 64-bit versions. To bring any science to this discussion, I created some code to actually test some of the available algorithms. I decided to test the built-in algorithms MD5, SHA1, SHA256 and SHA512. I also included a CRC32 implementation from force-net and a CRC64 implementation from DamienGKit . My results with a ~ 115 MB file are as follows:

Work in 32-bit mode .

Warming up phase :

CRC32: 296 MiB / s [9C54580A] in 390 ms.

CRC64: 95 MiB / s [636BCF1455BC885A] in 1212 ms.

MD5: 191 MiB / s [mm / JVFusWMKcT / P + IR4BjQ ==] in 604 ms.

SHA1: 165 MiB / s [WSFkGbnYte5EXb7kgp1kqbi2 ...] in 699 ms.

SHA256: 93 MiB / s [USKMHQmfMil8 / KL / ASyE6rm / ...] in 1240 ms.

SHA512: 47 MiB / s [Cp9cazN7WsydTPn + k4Xu359M ...] in 2464 ms.

Final run :

CRC32: 279 MiB / s [9C54580A] in 414 ms.

CRC64: 96 MiB / s [636BCF1455BC885A] in 1203 ms.

MD5: 197 MiB / s [mm / JVFusWMKcT / P + IR4BjQ ==] in 588 ms.

SHA1: 164 MiB / s [WSFkGbnYte5EXb7kgp1kqbi2 ...] in 707 ms.

SHA256: 96 MiB / s [USKMHQmfMil8 / KL / ASyE6rm / ...] in 1200 ms.

SHA512: 47 MiB / s [Cp9cazN7WsydTPn + k4Xu359M ...] in 2441 ms.


Work in 64-bit mode .

Warming up phase :

CRC32: 310 MiB / s [9C54580A] in 373 ms.

CRC64: 117 MiB / s [636BCF1455BC885A] in 986 ms.

MD5: 198 MiB / s [mm / JVFusWMKcT / P + IR4BjQ ==] in 584 ms.

SHA1: 184 MiB / s [WSFkGbnYte5EXb7kgp1kqbi2 ...] in 627 ms.

SHA256: 104 MiB / s [USKMHQmfMil8 / KL / ASyE6rm / ...] in 1112 ms.

SHA512: 149 MiB / s [Cp9cazN7WsydTPn + k4Xu359M ...] in 778 ms.

Final run :

CRC32: 292 MiB / s [9C54580A] in 396 ms.

CRC64: 119 MiB / s [636BCF1455BC885A] in 975 ms.

MD5: 199 MiB / s [mm / JVFusWMKcT / P + IR4BjQ ==] at 582ms.

SHA1: 192 MiB / s [WSFkGbnYte5EXb7kgp1kqbi2 ...] in 601 ms.

SHA256: 106 MiB / s [USKMHQmfMil8 / KL / ASyE6rm / ...] in 1091 ms.

SHA512: 157 MiB / s [Cp9cazN7WsydTPn + k4Xu359M ...] in 738 ms.

These results were obtained from a compiled ASP.NET project with a release running .NET v4.5.2. Both 32-bit and 64-bit results refer to the same computer. In Visual Studio, I changed the mode using Tools > Options > Projects and Solutions > Web Projects > Use the 64 bit version of IIS Express , and also changed the Platform target project.

We see that although the results fluctuate in bit-rates, CRC32 (by force-net) is the fastest, followed by Microsoft MD5 and SHA1. Curiously, there is no performance benefit when choosing DamienGKit CRC64 over integrated MD5 or SHA1. 64-bit execution seems to help SHA512 a lot, but only modestly with others.

To answer the OP question, it would seem that the integrated MD5 or SHA1 can provide the best balance for collision avoidance and performance.

My code is as follows:

 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 />"; 
+2
source

Test Results Analysis

I will further analyze the performance of PHP's built-in hashes, in addition to the analysis made earlier here by [Michael] [1] (see above), because this topic is quite interesting and has unexpected results.

The results are not so obvious or even surprising. A simple algorithm is CRC32, slower than a complex one is MD5. It seems that modern processors do not like certain old algorithms and execute them very slowly. The CRC32 CCIT ITU algorithm was relatively fast and efficient in the good old days when there were 300 BPS remote access modems. Now there is a modern algorithm specially designed for new equipment that can run much faster on the same hardware than old algorithms that are inherently unsuitable for new equipment, and even if you try to optimize them, they will in any case slow. For example, for algorithms in which each byte depends on the previous one, you cannot take advantage of 64-bit registers and process many bits in parallel.

You can see from other cryptographic libraries that confirm what we see in PHP - that CRC32 has almost the same maximum speed as MD5. Here is a link with the results of another library: https://www.cryptopp.com/benchmarks.html

OpenSSL shows similar results. At first glance, this may seem irrational, because the algorithm for CRC32 is much simpler than for MD5, but reality shows the opposite.

I just want to show how simple the CRC32 function is.

Here is the code that updates the CRCR32 counter with the following incoming byte (Delphi):

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

Here is the assembly code:

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

You can also deploy this code so that you get only 5 processor instructions for each byte:

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

You just need to load the rbx register with the next 8 bytes of data, and then repeat this code 8 times until you need to load the next 8 bytes into the 64-bit rbx register.

Here's the caller's routing, which calculates the CRC32 of the entire line:

  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; 

And here is how it is compiled into Delphi machine code - not very optimal, but rather simple - only 11 build commands for each byte, which, surprisingly, runs on Intel Core i5-6600 a little faster than the above assembler code even after the cycle has been canceled. As you can see, and all of these CRC32 CCIT ITU implementation instructions are straightforward, without loops or comparisons, there is only one comparison at the end of each byte. This is just a Delphi compiled code debugger, not a human-written build code.

  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 

Here is another version of the program code for CRC32: there are only 5 processor instructions for each byte, not 11, but it is essentially the same as the above assembler code, it just uses different registers and avoids the loop command, which again on i5 6600 is faster than two different instructions. You can find all the code in the CRC32 assembler function called from the C console application

  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 

Now compare it to MD5 using this highly optimized assembler code by Peter Savacki:

 ; 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 

The above code handles one call of 64 bytes of incoming data. It is called from the main procedure, which takes the preparation steps:

  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; 

And if your processor supports CRC32 operation codes (SSE 4.2), you can calculate checksums 10 times faster using this code:

  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 dword ptr 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 dword ptr eax,dword ptr [rdx] crc32 dword ptr eax,dword ptr [rdx+4] dec r8 lea rdx,rdx+8 jnz @1 @2: and rcx,7; jz @0 cmp rcx,4; jb @4 crc32 dword ptr eax,dword ptr [rdx] sub rcx,4 lea rdx,rdx+4 jz @0 @4: crc32 dword ptr eax,byte ptr [rdx] dec rcx; jz @0 crc32 dword ptr eax,byte ptr [rdx+1] dec rcx; jz @0 crc32 dword ptr eax,byte ptr [rdx+2] @0: not eax end; 

Please note that in my example, I use a buffer of only 5 KB in size to fit in the processor cache and exclude the influence of slow RAM on the speed of digest calculation.

In PHP, even in version 7, there seems to be no support for CRC32 hardware acceleration, although these instructions are supported on Intel and AMD processors with age. Intel has supported CRC32 since November 2008 (Nehalem (microarchitecture)), and AMD seems to have supported it since 2013.

My own tests confirming Michael's results

I tested various PHP hash functions on different configurations: (1) AMD FX-8320 (released in 2012) under Ubuntu with PHP 5 and (2) Intel Core i5-6600 released in 2015 under Windows with PHP 7. I also tested the OpenSSL test on this Intel Core i5-6600. In addition, I run tests of cryptographic procedures that we use in our software "The Bat!". written in Delphi. Although the main software is written in Delphi, the cryptographic routines we use are written on the Assembler processor for Intel (32-bit or 64-bit) or C.

I found out that our Delphi code shows very large differences in speed between different hash functions and data sizes. This is in contrast to PHP, where to a certain extent and with rare exceptions, all hash functions from the simplest CRC32 to the cryptographically strong MD5 have almost the same ascent rate.

So, here are the measurements I made on AMD FX-8320, PHP5, Ubuntu. I did two tests. Firstly, I spent 5000 iterations on a hash message consisting of only 5 bytes. By this small message size, I was going to check the duration of the initialization / completion steps of various algorithms and how this affects the overall performance. For some algorithms, such as CRC32, three are practically not finalization stages - the digest is always ready after each byte. Cryptographically strong functions, such as SHA1 or MD5 or others, have a finalization step that compresses a larger context to a smaller final digest. Secondly, I run 5000 iterations for a hash message of 5000 bytes in length. Both messages were filled in advance using pseudo-random bytes (they were not filled again after each iteration, they were filled only once, when the program was launched).

Results of my PHP hash speed test

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 

PHP script Intel Core i5-6600, 64- PHP7 Windows 10. :

  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 

, CRC32 PHP , MD5 . , 5000 5000 Intel Core i5-6600 PHP7 CRC32 MD5 (!). . .

, PHP MD5 SHA1, Ubuntu PHP5, 5000 5000 MD5 .

OpenSSL

OpenSSL Intel i5-660. -. , , , : , 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

Delphi Intel Core i5-6600 64- Windows 10, 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 0,001284 , 5000 5- MD5, 0,001499 SHA1. 5000 - PHP7 0.046208 MD5 0.050259 SHA-1.

Delphi, 0,0005015 5000 5- MD5 0.0050118 SHA1. 5000 - Delphi 0.0376252 secs MD5 0.1830871 SHA-1. , MD5 Delphi, SHA-1 . , Delphi 10 5- , 5000- SHA-1.

CRC32 CRC32C, Delphi , 10 1000 , PHP.

Conclusion

PHP . , PHP , - , . , : , MD2 , MD5. , MD2. PHP MD2 MD5 . MD5, -, PGP RFC-1991, , , , ETags . ( PHP , ), MD5 . PHP-, . (. ).

 <? 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(); } ?> 
0
source

, .

" - "

: " - ", . PHP, MD5 . CRC32 , , PHP 5 7. , PHP, MD5 , PHP, . MD5 , , .

AES (AES-NI), AES CBC . , MD-5, (128 ).

Tip. , PHP, base64_encode md5, , , .

-

, Delphi ++, CRC32C, Intel AMD.

Delphi, 5000 , 5000 , 0,0055349 . , - CRC32, , , , PHP.

, 4 , CRC32, 16 , MD5 MD5 . MD5 - PGP . , . - , , , , MD5 2017 , , -. , AES (AES-NI), AES-CBC . , .

,

, - - , 16 , , , , CRC32 4 ?

, , " - " (. ).

"memcached" , , , "memcached" , . , , , , .. . , , , : $UniqueKey = "$ Namespace | $Realm | $Application | $AppComonent | $ | $Key";

, 60 .

memcached (), + , 96 , 120 192 , 304- . , , , -, MD5. -, , . base-64-encode MD trailing "=" padding , , , Base64, , , "memcached" , :

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

-: , KB/S:

  • CRC32 CCIT, 32- : 50000000 64 7.2012 CRC32; 423.7874 ;
  • CRC32 CCIT, 64- : 50000000 64 7.1871 CRC32; 424,6137 ;
  • CRC32 CCIT, 32-, Delphi: 50000000 64 7.1350 CRC32; 427,7164 ;
  • CRC32 CCIT, 64-, Delphi: 50000000 64 7.3686 CRC32; 414,1570 ;
  • CRC32C, , 32- : 50000000 64 2.4866 CRC32C; 1227,2629 ;
  • CRC32C, 64- : 50000000 64 2,7694 CRC32C; 1101.9702 ;
  • CRC32C, 32-, SSE 4.2: 50000000 64 0,7099 CRC32C CPU SSE 4.2; 4298.7911 ;
  • CRC32C, 32-, SSE 4.2: 50000000 64 0,7510 CRC32, CPU SSE 4.2; 4063.6096 ;
  • MD5, 32-, : 50000000 64 4.4489 MD5; 685.9647
  • MD5, 64-, : 50000000 64 ; 4.4369 MD5; 687.8157 ;
  • AES, , 32-, : 50000000 64 23,6519 AES-CBC 128- ; 129.0280 ;
  • AES, , 64-, : 50000000 64 28,1875 AES-CBC 128- ; 108.2662 ;
  • AES, 64-, AES-NI: 50000000 64 1.6374 AES-CBC 128- ; 1863.8040 ;
  • AES, , 64- AES-NI: 50000000 64 1,6063 AES-CBC 128- ; 1899.8995 .

, , !

0
source

All Articles