Ich benötige die schnellste Hash-Funktion in Delphi 2009, die Hashwerte aus einer Unicode-Zeichenfolge erzeugt, die ziemlich zufällig in Buckets verteilt wird.Effizienteste Unicode-Hash-Funktion für Delphi 2009
ich ursprünglich mit Gabr ‚s HashOf Funktion von GpStringHash gestartet:
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 }
Aber ich fand, dass dies nicht gute Zahlen von Unicode-Strings erzeugte. Ich stellte fest, dass Gabr Routinen nicht zu Delphi aktualisiert 2009.
Dann entdeckte ich HashNameMBCS in SysUtils von Delphi 2009 und übersetzt es auf diese einfache Funktion (wobei „string“ ist ein Delphi 2009 Unicode-String):
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 }
ich dachte, das war ziemlich gut, bis ich an der CPU-Fenster und sah den Code Assembler es erzeugt:
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
Dies scheint als Code des Gabr ziemlich viel mehr Assembler-Code zu enthalten.
Geschwindigkeit ist von entscheidender Bedeutung. Kann ich irgendetwas tun, um den von mir geschriebenen Pascal-Code oder den Assembler, den mein Code generiert hat, zu verbessern?
Follow-up.
Ich ging schließlich mit der HashOf-Funktion basierend auf SysUtils.HashNameMBCS. Es scheint eine gute Hashverteilung für Unicode-Strings zu geben und scheint ziemlich schnell zu sein.
Ja, es wird eine Menge Assembler-Code generiert, aber der Delphi-Code, der ihn erzeugt, ist so einfach und verwendet nur Bit-Shift-Operationen, daher ist es schwer zu glauben, dass es nicht schnell gehen würde.
In Ihrem endgültigen HashOf sollte ich von 1 bis Länge (Schlüssel) gehen. – gabr
@gabr: Danke. Ich sehe jetzt, dass ich das "Follow-up" geschrieben habe und nicht einmal realisiert habe, dass ich die gleiche Funktion benutzt habe wie meine Frage, außer dass ich den Fehler in meinem Follow-up gemacht habe. Ich werde das umschreiben. – lkessler