是否有HASH3的Delphi实现?

是否有HASH3的Delphi实现?,delphi,murmurhash,Delphi,Murmurhash,是否有3的Delphi实现?我试着自己实现它,但实际上我的实现比。这正常吗? 还有其他的实现吗 这是我的: function MurMur3_32(const S: AnsiString; const Seed: LongWord=$9747b28c): LongWord; const c1 = $cc9e2d51; c2 = $1b873593; r1 = 15; r2 = 13; m = 5; n = $e6546b64; var hash: LongWord

是否有3的Delphi实现?我试着自己实现它,但实际上我的实现比。这正常吗? 还有其他的实现吗

这是我的:

function MurMur3_32(const S: AnsiString; const Seed: LongWord=$9747b28c): LongWord;
const
  c1 = $cc9e2d51;
  c2 = $1b873593;
  r1 = 15;
  r2 = 13;
  m = 5;
  n = $e6546b64;
var
    hash: LongWord;
    len: LongWord;
    k, k2: LongWord;
    data: Integer;
begin
  Hash := Seed;

    len := Length(S);

    //The default seed, $9747b28c, is from the original C library

    // Initialize the hash to a 'random' value
    hash := seed xor len;

    // Mix 4 bytes at a time into the hash
    data := 1;

    while(len >= 4) do
    begin
        k := PLongWord(@S[data])^;

        k := k*c1;
        k := (k shl r1) or (k shr (32 - r1));
        k := k*c2;


        hash := hash xor k;
        hash := ((hash shl r2) or (hash shr (32 - r2))) * m + n;

        Inc(Data, 4);
        Dec(len, 4);
    end;

    k2 := 0;

    {   Handle the last few bytes of the input array
            S: ... $69 $18 $2f
    }
    Assert(len <= 3);
    if len = 3 then
        k2 := k2 xor (LongWord(s[data+2]) shl 16);
    if len >= 2 then
        k2 := k2 xor (LongWord(s[data+1]) shl 8);
    if len >= 1 then
    begin
        k2 := k2 xor (LongWord(s[data]));
        k2 := k2 * c1;
        k2 := (k2 shl r1) or (k2 shr (32 - r1));
        k2 := k2 * c2;
        hash := hash xor k2;
    end;

    // Do a few final mixes of the hash to ensure the last few
    // bytes are well-incorporated.
    len := Length(S);

    hash := hash xor len;
    hash := hash xor (hash shr 16);
    hash := hash * $85ebca6b;
    hash := hash xor (hash shr 13);
    hash := hash * $c2b2ae35;
    hash := hash xor (hash shr 16);


    Result := hash;

end;
函数332(const S:AnsiString;const Seed:LongWord=$9747b28c):LongWord;
常数
c1=cc9e2d51美元;
c2=1b873593美元;
r1=15;
r2=13;
m=5;
n=e6546b64美元;
变量
哈希:长单词;
len:长词;
k、 k2:长单词;
数据:整数;
开始
散列:=种子;
长度:=长度(S);
//默认种子$9747b28c来自原始C库
//将哈希初始化为“随机”值
散列:=种子异或len;
//在散列中一次混合4个字节
数据:=1;
而(len>=4)do
开始
k:=PLongWord(@S[data])^;
k:=k*c1;
k:=(k shl r1)或(k shr(32-r1));
k:=k*c2;
散列:=散列异或k;
散列:=((散列shlr2)或(散列shr(32-r2)))*m+n;
公司(数据,4);
12月(len,4);
结束;
k2:=0;
{处理输入数组的最后几个字节
S:…$69$18$2f
}
断言(len=2)
k2:=k2xor(长字(s[data+1])shl8);
如果len>=1,则
开始
k2:=k2 xor(长字[数据]);
k2:=k2*c1;
k2:=(k2 shl r1)或(k2 shr(32-r1));
k2:=k2*c2;
散列:=散列异或k2;
结束;
//对散列进行最后几次混合,以确保最后几次
//字节被很好地合并。
长度:=长度(S);
hash:=hash xor len;
散列:=散列异或(散列shr 16);
散列:=散列*$85ebca6b;
散列:=散列异或(散列shr 13);
散列:=散列*$c2b2ae35;
散列:=散列异或(散列shr 16);
结果:=散列;
结束;

免责声明:我不知道
种子
值是否正确。

我的FastDefaults单元中有一个HASH3实现

以下是第3部分的源代码:

{$pointermath on}
function MurmurHash3(const [ref] HashData; Len: integer; Seed: integer = 0): integer;
const
  c1 = $CC9E2D51;
  c2 = $1B873593;
  r1 = 15;
  r2 = 13;
  m = 5;
  n = $E6546B64;
  f1 = $85EBCA6B;
  f2 = $C2B2AE35;
{$IFDEF purepascal}
var
  i, Len2: integer;
  k: cardinal;
  remaining: cardinal;
  Data: PCardinal;
label
  case1, case2, case3, final;
begin
  Result:= Seed;
  Data:= @HashData;
  for i:= 0 to (Len shr 2) - 1 do begin
    k:= Data[i];
    k:= k * c1;
    k:= (k shl r1) or (k shr (32 - r1));
    k:= k * c2;
    Result:= Result xor k;
    Result:= (Result shl r2) or (Result shr (32 - r2));
    Result:= Result * m + n;
  end; {for i}
  Len2:= Len;
  remaining:= 0;
  case Len and $3 of
    1: goto case1;
    2: goto case2;
    3: goto case3;
    else goto final;
  end;
case3:
  dec(Len2);
  inc(remaining, PByte(Data)[Len2] shl 16);
case2:
  dec(Len2);
  inc(remaining, PByte(Data)[Len2] shl 8);
case1:
  dec(Len2);
  inc(remaining, PByte(Data)[Len2]);
  remaining:= remaining * c1;
  remaining:= (remaining shl r1) or (remaining shr (32 - r1));
  remaining:= remaining * c2;
  Result:= Result xor remaining;
final:
  Result:= Result xor Len;

  Result:= Result xor (Result shr 16);
  Result:= Result * f1;
  Result:= Result xor (Result shr 13);
  Result:= Result * f2;
  Result:= Result xor (Result shr 16);
end;
{$ELSE}
{$REGION 'asm'}
{$IFDEF CPUx86}
  asm
    push EBX
    push EDI
    push ESI
    xchg ECX,EDX
    //EAX = data
    //ECX = count in bytes
    //EDX = seed
    mov  ESI,ECX
    shr  ECX,2
    jz @remaining_bytes
  @loop:
    mov  EDI,[EAX]
    imul EDI,EDI,c1
    rol  EDI,r1
    imul EDI,EDI,c2
    xor  EDX,EDI
    rol  EDX,r2
    lea  EDX,[EDX*4+EDX+n]
    lea  EAX,[EAX+4]
    dec  ECX
    jnz @loop
  @remaining_bytes:
    mov  ECX,ESI
    and  ECX,$3
    jz @finalization
    xor  EBX,EBX
    dec  ECX
    mov  BL,byte ptr [EAX+ECX]
    jz @process_remaining
    shl  EBX,8
    dec  ECX
    mov  BL,byte ptr [EAX+ECX]
    jz @process_remaining
    shl  EBX,8
    mov  BL,byte ptr [EAX]
  @process_remaining:
    imul EBX,EBX,c1
    rol  EBX,r1
    imul EBX,EBX,c2
    xor  EDX,EBX
  @finalization:
    xor  EDX,ESI
    mov  EAX,EDX
    shr  EDX,16
    xor  EDX,EAX
    imul EDX,EDX,f1
    mov  EAX,EDX
    shr  EDX,13
    xor  EDX,EAX
    imul EDX,EDX,f2
    mov  EAX,EDX
    shr  EDX,16
    xor  EAX,EDX
    pop  ESI
    pop  EDI
    pop  EBX
end;
{$ENDIF}
{$IFDEF CPUx64}
asm
  push RBX
  push RDI
  push RSI
  mov  RAX,RCX
  mov  RCX,RDX
  mov  RDX,R8
  //RAX = data
  //RCX = count in bytes
  //RDX = seed
  mov  ESI,ECX
  shr  ECX,2
  jz @remaining_bytes
@loop:
  mov  EDI, dword ptr [RAX]
  imul EDI,EDI,c1
  rol  EDI,r1
  imul EDI,EDI,c2
  xor  EDX,EDI
  rol  EDX,r2
  lea  EDX,dword ptr [EDX*4+EDX+n] // *5 + n
  lea  RAX,qword ptr [RAX+4]
  dec  ECX
  jnz @loop
@remaining_bytes:
  mov  ECX,ESI
  and  ECX,$3
  jz @finalization
  xor  RBX,RBX
  dec  ECX
  mov  BL,byte ptr [RAX+RCX]
  jz @process_remaining
  shl  EBX,8
  dec  ECX
  mov  BL,byte ptr [RAX+RCX]
  jz @process_remaining
  shl  EBX,8
  mov  BL,byte ptr [RAX]
@process_remaining:
  imul EBX,EBX,c1
  rol  EBX,r1
  imul EBX,EBX,c2
  xor  EDX,EBX
@finalization:
  xor  EDX,ESI
  mov  EAX,EDX
  shr  EDX,16
  xor  EDX,EAX
  imul EDX,EDX,f1
  mov  EAX,EDX
  shr  EDX,13
  xor  EDX,EAX
  imul EDX,EDX,f2
  mov  EAX,EDX
  shr  EDX,16
  xor  EAX,EDX
  pop  RSI
  pop  RDI
  pop  RBX
end;
{$ENDIF}
{$ENDREGION}
{$ENDIF}
goto用于模拟C的FallThrough switch/case语句。
这种代码更容易在asm中编写,它比asm Delphi生成的寄存器使用率更好

为什么您的代码很慢
我唯一能看到问题的地方是:

Project42.dpr.54: while(len >= 4) do    //a for loop is faster.
00420E17 83FE04           cmp esi,$04
00420E1A 7242             jb $00420e5e

//This translates into inefficient code
Project42.dpr.56: k := PLongWord(@S[data])^;  
00420E1C 8B0424           mov eax,[esp]
00420E1F 8D4438FF         lea eax,[eax+edi-$01]
00420E23 8B00             mov eax,[eax]
三个间接内存引用和!!
请参见此处了解更多信息:不要在意已接受的答案,请参见第二个答案。
双字读取应始终在dword边界上进行。
@pointer^
技巧使编译器添加额外的间接级别(以及额外的内存往返(Oops))。
使用
{$pointermath on}
并将指针作为数组寻址。

整数!=指针
使用
integer
存储指针也是错误的。
它将在X64中中断。改用NativeUInt。

我的版本中的等效代码转换为:

Project42.dpr.128: Data:= @HashData;
00420FCD 89442404         mov [esp+$04],eax
Project42.dpr.129: for i:= 0 to (Len shr 2) - 1 do begin
00420FD1 8B1C24           mov ebx,[esp]
00420FD4 C1EB02           shr ebx,$02
00420FD7 4B               dec ebx
00420FD8 85DB             test ebx,ebx
00420FDA 7C3E             jl $0042101a
00420FDC 43               inc ebx
00420FDD 33D2             xor edx,edx
////------------------------------------------///
Project42.dpr.130: k:= Data[i];
00420FDF 8B442404         mov eax,[esp+$04]
00420FE3 8B0490           mov eax,[eax+edx*4]
请注意,不必要的间接内存引用和读取是正确对齐的

当然,asm版本稍好一些,但是asm和pascal版本之间的差异应该小于两个pascal版本之间的差异

这就是我认为你的表现被浪费的地方。
未对齐的读取占用了X86上大量(浪费的)周期。
在其他处理器上,这种放缓甚至更为严重

代码的其他问题
将实现限制为字符串是一件坏事。
如果我想散列一个记录怎么办?
不要强制二进制数据进入字符串。
字符串不适合二进制数据。
只需使用非类型化的参数和指针

种子值
我认为没有一个正确的种子值。按照我的理解,种子就在那里,这样你就可以将调用链接到杂音散列。
您使用第一个散列的输出作为第二次运行的种子。
这样,您可以输入两个变量,并获得相同的输出,就像您一次性处理了这两个变量一样


让我知道它们对您的代码的执行情况。

这个问题可能是离题的,不清楚您会问什么。但我的主要意见是想知道为什么您要将二进制数据存储在字符串中?使用字节数组。我用字符串制作它只是为了尝试实现……因为每个人都会根据自己的需要调整它。我需要o散列字符串…你对这个问题有什么不理解?我理解这个问题,但它是离题的,因为你要求其他库,而不清楚(技术术语),因为你问了一个模糊的“它可以改进吗?”。再说一遍,为什么要把二进制数据放在字符串中?如果字符串经过编码转换会发生什么?我认为核心问题很好:
为什么我的直接实现很慢?
。当然混合了其他一些次要问题,比如
给我其他实现
让它脱离主题。但我不认为这有损from核心问题。@Johan,核心问题不清楚,因为没有提供时间细节。谢谢你的精彩解释!你的ASM代码非常快!谢谢分享!PS:我注意到pascal代码中有一个错误…你没有初始化“剩余的”变量。再次非常感谢!FWIW Integer可以很好地保存32位指针。有符号或无符号并不重要,除非启用范围/溢出检查。@Alex,是的,不客气。在
剩余