Мне нужна самая быстрая хэш-функция, возможная в Delphi 2009, которая будет создавать хэшированные значения из строки Unicode, которая будет довольно случайно распределяться в сегменты.
Я изначально начал с Gabr HashOf-функции из GpStringHash:
function HashOf(const key: string): cardinal;
asm
xor edx,edx { result := 0 }
and eax,eax { test if 0 }
jz @End { skip if nil }
mov ecx,[eax-4] { ecx := string length }
jecxz @End { skip if length = 0 }
@loop: { repeat }
rol edx,2 { edx := (edx shl 2) or (edx shr 30)... }
xor dl,[eax] { ... xor Ord(key[eax]) }
inc eax { inc(eax) }
loop @loop { until ecx = 0 }
@End:
mov eax,edx { result := eax }
end; { HashOf }
Но я обнаружил, что это не дает хорошие числа из строк Unicode. Я заметил, что процедуры Габра не были обновлены до Delphi 2009.
Затем я обнаружил HashNameMBCS в SysUtils Delphi 2009 и преобразовал его в эту простую функцию (где «string» - это строка Unicode Delphi 2009):
function HashOf(const key: string): cardinal;
var
I: integer;
begin
Result := 0;
for I := 1 to length(key) do
begin
Result := (Result shl 5) or (Result shr 27);
Result := Result xor Cardinal(key[I]);
end;
end; { HashOf }
Я думал, что это очень хорошо, пока не посмотрел на окно процессора и не увидел сгенерированный им код ассемблера:
Process.pas.1649: Result := 0;
0048DEA8 33DB xor ebx,ebx
Process.pas.1650: for I := 1 to length(key) do begin
0048DEAA 8BC6 mov eax,esi
0048DEAC E89734F7FF call $00401348
0048DEB1 85C0 test eax,eax
0048DEB3 7E1C jle $0048ded1
0048DEB5 BA01000000 mov edx,$00000001
Process.pas.1651: Result := (Result shl 5) or (Result shr 27);
0048DEBA 8BCB mov ecx,ebx
0048DEBC C1E105 shl ecx,$05
0048DEBF C1EB1B shr ebx,$1b
0048DEC2 0BCB or ecx,ebx
0048DEC4 8BD9 mov ebx,ecx
Process.pas.1652: Result := Result xor Cardinal(key[I]);
0048DEC6 0FB74C56FE movzx ecx,[esi+edx*2-$02]
0048DECB 33D9 xor ebx,ecx
Process.pas.1653: end;
0048DECD 42 inc edx
Process.pas.1650: for I := 1 to length(key) do begin
0048DECE 48 dec eax
0048DECF 75E9 jnz $0048deba
Process.pas.1654: end; { HashOf }
0048DED1 8BC3 mov eax,ebx
Кажется, он содержит немного больше ассемблерного кода, чем код Габра.
Скорость имеет значение. Могу ли я что-нибудь сделать для улучшения написанного мной кода на Паскале или сгенерированного моим кодом ассемблера?
Followup.
Я наконец-то выбрал функцию HashOf, основанную на SysUtils.HashNameMBCS. Похоже, он дает хорошее распределение хеша для строк Unicode и выглядит довольно быстро.
Да, генерируется много кода на ассемблере, но код Delphi, который генерирует его, настолько прост и использует только операции сдвига битов, поэтому трудно поверить, что он не будет быстрым.