Выбор хэш-функции для лучшей производительности

Мне нужно сравнить много файлов (некоторые могут быть большими, некоторые-маленькими) по сети. Поэтому я планирую хэшировать каждый файл на каждом клиенте и отправлять только хэш-значение по сети.
основная цель здесь-производительность. Это подразумевает минимальный сетевой трафик. Проблема не в безопасности.
также должны быть" нулевые " столкновения так как я не хочу когда-либо ошибочно рассматривать два разных файла как идентичные. Говоря это, я знаю, что теоретически всегда есть столкновения, я просто хочу, чтобы шанс когда-либо практически встретиться с ними был абсолютно ничтожным.

Итак, мой вопрос: какая хэш-функция .net лучше всего подходит для этой задачи?

Я думал использовать буферизованный читатель и MD5CryptoServiceProvider (поскольку CNG может быть доступен не на всех клиентах).

есть ли способ получить лучшую производительность, чем это? (возможно, используя некоторую внешнюю библиотеку?)

6 ответов


Это зависит от количества файлов, которые вы имеете.

вероятность столкновения P(collision) = c/2^N (в идеальной хэш-функции), где c ваше количество сообщений (файлов) и N - это количество бит в вашем алгоритме столкновения.

поскольку реальные хэш-функции не идеальны, у вас есть два варианта: оптимизация скорости и оптимизация для предотвращения столкновений.

в первом случае вы хотите использовать CRC32 в. CRC32 в очень часто, но, в зависимости от количества файлов, которые у вас есть, может быть недостаточно: вы гарантированно столкнетесь с ~4,3 миллиардами сообщений (32 эффективных бита), но на практике вы можете столкнуться с вашим первым столкновением с ~10 миллионами сообщений. CRC32 имеет очень быстрые реализации (SSE 4.2 даже имеет аппаратную инструкцию для него). CRC64 имеет гораздо меньшую вероятность столкновения, но не широко используется, поэтому, если вы хотите больше избежать столкновения, чем CRC32, вам лучше взглянуть на криптографический хэш функции.

если вы хотите избежать столкновений, жертвуя скоростью, вам понадобятся криптографические хэш-функции, из которых MD5 (128 бит), SHA-1 (160 бит) и SHA-2 (обычно SHA-256 или SHA-512) наиболее широко используются и имеют быстрые реализации. Очень эффективные алгоритмы поиска хэш-коллизий для MD5 доступны, но если вы вводите случайные сообщения, вы получите как можно ближе к P(collision) = c/2^128 как вы когда-нибудь получите, все еще работает в разумное время.


хэш-функции не построены для скорости, поэтому они не являются хорошими кандидатами на работу. Их преимущество (криптографической защиты) также не имеет значения в этом случае.

вы должны изучить использование CRC или другой функции контрольной суммы; существует список часто используемыхздесь. HashLib есть готовые реализации.


Я считаю, что вы неправильно понимаете цель хэша в этом случае.

он должен иметь быстрый "это не то же самое " проверить. Хэши не могут сказать вам, равны ли две вещи, потому что они будет наехать.

поэтому, учитывая, как очень редко простой CRC будет сталкиваться и насколько быстрее он будет над большим количеством файлов, это лучшее решение.

Если два хэша или CRC одинаковы, ваша реакция должна быть ровно то же самое: проверьте равенство по фактическому содержанию. Вы даже можете хэшировать / CRC подмножество равного размера - и проверять размеры файлов-для быстрого "исключения" проверок после соответствия CRC.


Если вы ожидаете иметь много равных файлов, хэш по-прежнему не устраняет необходимость проверять иначе, но это уменьшит необходимость. Ты все равно захочешь провести какую-нибудь другую проверку. Равенство хэша, плюс совпадение длины файла, плюс частичное равенство хэша (хэширование первый x байты файла, например) может быть достаточно хорошо, в зависимости от ваших потребностей.


Я был в аналогичной ситуации, когда мне нужен алгоритм хэширования .NET. Мне это нужно для кэширования ответов сервера, где скорость важнее безопасности. Когда я добрался до этой темы, я заметил спекуляции о различиях производительности в выборе алгоритма и в 32-битном и 64-битном исполнении. Чтобы внести некоторую науку в эту дискуссию, я создал некоторый код, чтобы фактически протестировать некоторые из доступных алгоритмов. Я решил протестировать встроенные алгоритмы MD5, SHA1, SHA256 и SHA512. Я также включена реализация CRC32 от сила-продажи и реализация CRC64 из DamienGKit. Мои результаты с файлом ~115MB следующие:

работает в 32-битном режиме.

разминка:

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

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

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

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

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

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

финальный запуск:

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

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

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

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

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

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


работает в 64-разрядном режиме.

разминка:

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

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

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

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

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

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

финальный запуск:

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

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

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

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

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

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

эти результаты были получены из скомпилированной версии-сборки ASP.NET проект под управлением .NET v4.5.2. Как 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 от Microsoft. Любопытно, что нет никакого преимущества в производительности при выборе Crc64 DamienGKit над встроенным 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 />";

анализ результатов тестирования

я дополнительно проанализирую производительность встроенных хешей PHP, в дополнение к анализу, сделанному ранее здесь [Michael][1] (см. выше сообщение), потому что эта тема довольно интересна и имеет неожиданные результаты.

результаты не настолько очевидны или даже удивительны. Простой алгоритм-CRC32, медленнее, чем сложный-MD5. Похоже, что современные процессоры не любят конкретных старых алгоритмов и выполняют их очень медленно. Алгоритм CRC32 CCIT ITU был относительно быстрым и эффективным в старые добрые времена, когда было 300 модемов удаленного доступа BPS. Теперь есть современный алгоритм, специально разработанный для нового оборудования, который может работать намного быстрее на том же оборудовании, чем старые алгоритмы, которые по своей сути непригодны для нового оборудования, и даже если вы попытаетесь их оптимизировать, они все равно будут медленными. Например, для алгоритмов, где каждый байт зависит от предыдущего свидания, и вы не можете воспользоваться преимуществами 64-разрядных регистров и обрабатывать много битов параллельно.

вы можете увидеть из других криптографических библиотек, которые подтверждают то, что мы видим в PHP - что CRC32 имеет почти такую же скорость thoroughoutput как MD5. Вот ссылка на результаты другой библиотеки:https://www.cryptopp.com/benchmarks.html

OpenSSL показывает аналогичные результаты. На первый взгляд это может показаться нерациональным, ведь алгоритм для CRC32 намного проще и чем для MD5, но реальность показывает противоположный.

я просто хочу показать, насколько проста функция CRC32.

вот код, который обновляет счетчик CRCR32 со следующим входящим байтом (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;

вот этот код на сборку:

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

вы также можете развернуть этот код, так что вы получите только 5 инструкций процессора на байт:

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

вам просто нужно загрузить регистр rbx со следующими 8 байтами данных, а затем повторить этот код 8 раз, пока вам не понадобится загрузите следующие 8 байт в 64-разрядный регистр rbx.

здесь маршрутизация вызывающего абонента, которая вычисляет 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 CCIT ITU прямо вперед, без циклов или сравнений, есть только одно сравнение в конце каждого байта. Это всего лишь отладчик скомпилированного кода Delphi, а не код сборки, написанный человеком.

    CRC32.pas.78: begin
                                    push esi
                                    push edi
    CRC32.pas.80: if Size > 0 then
                                    test edx,edx
                                    jbe 500601
    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 500601
                                    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+7dec]
                                    shr ecx,
                                    xor edx,ecx
                                    mov ecx,edx
                                    inc esi
    CRC32.pas.83: for i := 0 to Size - 1 do
                                    dec eax
                                    jnz 5005e6
    CRC32.pas.86: Result := C;
                                    mov eax,ecx
    CRC32.pas.87: end;
                                    pop edi
                                    pop esi
                                    ret

вот еще один вариант кода assember для CRC32, всего 5 команд процессора для каждого байта, а не 11, но это по существу то же самое, что и приведенный выше код ассемблера, просто использует разные регистры и избегает команды "loop", которая снова находится на i5-6600 быстрее, чем две разные инструкции. Вы можете найти весь код в функция ассемблера CRC32 вызывается из консольного приложения C

             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, с этим высоко оптимизированным ассемблерным кодом Питера Савацки:

; 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

вышеуказанный код обрабатывает при одном вызове 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 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;

обратите внимание, что в моем примере я использую буфер всего 5 КБ, чтобы вписаться в кэш процессора и исключить влияние более медленной ОЗУ на скорость вычисления дайджеста.

в PHP, даже в версии 7, Кажется, нет поддержки аппаратного ускорения CRC32, хотя эти инструкции поддерживаются на процессорах Intel и AMD с давних пор. Поддержка Intel CRC32 с ноября 2008 года (Nehalem (микроархитектура)) и AMD, похоже, поддерживают его с 2013 года.

мои собственные тесты, которые подтверждают результаты Майкла

я тестировал различные хэш-функции PHP в разных конфигурациях: (1) AMD FX-8320 (выпущен в 2012 году) под Ubuntu с PHP 5 и (2) Intel Core i5-6600, выпущенный в 2015 году под Windows с PHP 7. Я также запустил OpenSSL-тест на этом Intel Core i5-6600. Кроме того, я провожу тесты криптографических процедур, которые мы используем в нашем программном обеспечении "Летучая мышь!" написанный на Delphi. Хотя основное программное обеспечение написано на Delphi, криптографические процедуры, которые мы используем, написаны на ассемблере для процессора Intel (32-бит или 64-бит) или в C.

я узнал, что наш код Delphi показывает очень большие различия в скорости между различными хэш-функциями и размерами данных. Это в отличие от PHP, где в определенной степени и за редкими исключениями все хэш-функции от простейших CRC32 до некогда криптографически сильных MD5 имеют почти одинаковый throughoutput скорости.

Итак, вот измерения, которые я сделал на 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

затем я запускаю тот же PHP-скрипт под 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 под 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 потребовалось 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 сек с MD5 и 0.1830871 сек с SHA-1. Как вы видите, MD5 работает намного быстрее в Delphi, но SHA-1 примерно такой же. Кроме того, Delphi примерно в 10 раз быстрее на 5-байтовом сообщении, но как около 5000-байтовых сообщений, это примерно то же самое или даже медленнее с SHA-1.

но когда дело доходит до CRC32 и CRC32C, Delphi непобедим, от 10 до 1000 раз быстрее, чем PHP.

вывод

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

я столкнулся с ограничением одного размера сообщения, поэтому я продолжу в этом втором сообщении.

заключительное примечание к вопросу "выбор хэш-функции для лучшей производительности"

что касается исходного вопроса: "выбор хэш-функции для лучшей производительности", мое мнение следующее. Если вы используете PHP, пожалуйста, рассмотрите возможность использования MD5 для расчета различных хэшей и дайджестов. Он не имеет почти никакой разницы в скорости сравнивая к CRC32, на по крайней мере, как это в настоящее время реализовано в PHP 5 и 7. В общем, если вы используете PHP, MD5 имеет значительную разницу в производительности по сравнению с другими хэшами, доступными в PHP, когда дело доходит до больших сообщений. Преимущество MD5 в том, что он генерирует относительно небольшой размер дайджеста, и это очень быстро.

если у вас есть аппаратная реализация AES (AES-NI), вы можете использовать AES в режиме CBC для создания дайджестов. Это будет намного быстрее, чем MD-5, с тем же размером дайджеста (128 бит.)

Совет: Если вам нужен еще меньший дайджест, но в текстовой форме для PHP, используйте base64_encode двоичного вывода md5, он создает более короткие строки результатов, чем шестнадцатеричная кодировка, используемая по умолчанию.

какие из хэш-функций использовать с языка программирования низкого уровня

Если вы используете язык программирования, такой как Delphi или c++, найдите хорошую реализацию CRC32C, которую можно запустить с аппаратным ускорением на современных процессорах Intel и AMD.

Как вы видите из моих результатов теста Delphi, наш код рассчитал контрольные суммы 5000 сообщений, каждый 5000 байт длиной, и в целом заняло всего 0,0055349 секунд. Это примерно в десять раз быстрее, чем наша не аппаратная реализация CRC32, которая, в свою очередь, все еще намного быстрее, чем одна реализованная в PHP.

Если вам нужны большие дайджесты, а не только 4 байта, которые производит CRC32, но по крайней мере 16 байтов, рассмотрите возможность поиска высокопроизводительного MD5 внедрение и использование MD5 для создания дайджестов. MD5 был разработан как криптографическая хэш-функция и использовался в шифровании PGP и цифровых подписях. Как это больше не подходит для шифрования, но для дайджестов сообщений это все еще нормально. Эта хэш-функция больше не может использоваться для криптографии, потому что столкновения очень легко найти, но если вам нужно использовать его только для вашей собственной контрольной суммы, где атаки столкновения не являются проблемой, я рекомендую использовать MD5 даже сегодня в 2017 году, при условии, что вы нашли быструю реализацию этой знаменитой хэш-функции. Если вы боитесь столкновений, и у вас есть аппаратная реализация AES (AES-NI), то используйте AES-CBC для дайджестов. Просто убедитесь, что вы реализуете padding правильно.

практический пример, где хэши с большими дайджестами могут быть полезны

позвольте мне объяснить, почему кому-то может понадобиться хэш-функция с большим размером дайджеста, например 16 байт, когда вы можете, на первый взгляд, использовать более короткий однажды, как CRC32 всего с 4 байтами?

Если бы большие хэши не были проблемой, не было бы никаких вопросов, таких как "выбор хэш-функции для лучшей производительности" (см. начальный пост).

считайте, что у вас есть доверенная среда серверов приложений и серверов "memcached", где все приложения имеют доступ ко всем данным, используемым на серверах под управлением демона "memcached", и данные доступны с помощью простых текстовых ключей. Я видел, как люди изобретали более длинные струны в заказах для ключи должны быть уникальными в разных областях, приложениях, т. е. не перекрываться. Поэтому они обычно соглашаются, по соглашению между всеми программистами и администраторами, которые поддерживают кластер или группу кластеров, что ключ должен состоять из разных обязательных сегментов, таких как: $UniqueKey = "$Namespace|$Realm|$Application|$AppComonent|$User|$Key";

Это yeilds к очень длинным клавишам, около 60 символов или более.

для демона "memcached" данные хранятся в слябы записей фиксированного размера (куски), и это длина ключа+значение, которые составляют размер одного куска, поэтому слябы небольших кусков 96 байт, 120 байт и 192 байта почти пусты, в то время как первый сляб действительно используется для 304-байтовых кусков. Чтобы избежать этой неэффективности более длинных ключей, вы можете, по соглашению, всегда переварить все эти ключи с помощью предопределенной хэш-функции, такой как MD5. Если все разработчики будут использовать один и тот же формат ключей и всегда будут хэшировать эти ключи определенная хэш-функция, нет практического риска того, что ключи могут перекрываться в этой доверенной среде. Вы будете просто base-64-кодировать двоичный вывод 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

выкладка результатов испытаний по-разному

позвольте мне также показать вам результаты теста производительности хэша в по-разному: делится на прошедшее время, поэтому вы видите конкретный хэш через вывод в КБ / с:

  • CRC32 CCIT benchmark, 32-разрядный ассемблер: 50000000 блоков данных 64 байты заняли 7.2012 секунды для обработки CRC32; 423.7874 мегабайт в второй;
  • CRC32 CCIT benchmark, 64-битный ассемблер: 50000000 блоков данных 64 байты заняли 7,1871 секунды для обработки CRC32; 424,6137 мегабайт в второй;
  • CRC32 в тест Всеобъемлющей конвенции о международном терроризме, 32-разрядная, чистая Делфи: 50000000 блоков данных 64 байты заняли 7.1350 секунд для обработки CRC32; 427.7164 мегабайт в второй;
  • CRC32 CCIT benchmark, 64-разрядный, чистый Delphi: 50000000 блоков данных 64 для обработки байтов CRC32 потребовалось 7.3686 секунд; 414.1570 мегабайт в секунду;
  • crc32c тест реализации программного обеспечения, 32-битный ассемблер: 50000000 блоков данных 64 байт заняло 2,4866 секунды для обработки CRC32C; 1227.2629 мегабайт в секунду;
  • программное обеспечение CRC32C тест реализации, 64-битный ассемблер: 50000000 блоки данных 64 байт заняли 2,7694 секунды для обработки CRC32C; 1101.9702 мегабайт в секунду;
  • crc32c аппаратная реализация benchmark, 32-бит, SSE 4.2: 50000000 блоки данных 64 байта взял 0.7099 секунд, чтобы процесс CRC32C использование команды SSE 4.2 CPU; 4298.7911 мегабайт в секунду;
  • crc32c аппаратная реализация benchmark, 32-бит, SSE 4.2: 50000000 блоки данных 64 байта взял 0.7510 секунд процесс CRC32 с использованием команда процессора SSE 4.2; 4063.6096 мегабайт в секунду;
  • MD5 benchmark, 32-бит, ассемблер: 50000000 блоков данных 64 байта потребовалось 4.4489 секунд для обработки MD5; 685.9647 мегабайт в секунду
  • MD5 benchmark, 64-бит, ассемблер: 50000000 блоков данных 64 байта; потребовалось 4.4369 секунд для обработки MD5; 687.8157 мегабайт в секунду;
  • AES benchmark, реализация программного обеспечения, 32-бит, ассемблер: 50000000 блоки данных 64 байты заняли 23.6519 секунд для обработки в AES-CBC с 128-битным ключом; 129.0280 мегабайт в секунду;
  • AES benchmark, программная реализация, 64-разрядная, ассемблер: 50000000 блоки данных 64 байт заняли 28.1875 секунд для обработки в AES-CBC с 128-битным ключом; 108.2662 мегабайт в секунду;
  • тест аппаратной реализации AES, 64-разрядный, AES-NI: 50000000 данных блоки 64 байт заняли 1,6374 секунды для обработки в AES-CBC с 128-битный ключ; 1863.8040 мегабайт в второй;
  • AES benchmark, аппаратная реализация, 64-бит, AES-NI: 50000000 данных блоки 64 байт заняли 1,6063 секунды для обработки в AES-CBC с 128-битный ключ; 1899.8995 мегабайт в секунду.

Я уверен, что вы нашли проблему хэшей очень важной, так что это!