While I was converting MurmurHash3, I noticed the FNV32a procedure can be made significantly faster.
First a remark, the return type of FNV32a should be a long instead of a quad.
Here's the improved FNV32a code
Code:
Procedure.l FNV32a(*key, len.l)
EnableASM
MOV edx, *key
MOV ecx, len
!push ebx
!mov eax, 2166136261
!fnv32a_loop:
!movzx ebx, byte [edx]
!xor eax, ebx
!imul eax, 0x01000193
!inc edx
!dec ecx
!jnz fnv32a_loop
!pop ebx
DisableASM
ProcedureReturn
EndProcedure
I also created an alternative one for zero terminated strings that doesn't require a length parameter
Code:
Procedure FNV32s(*key)
EnableASM
CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
MOV edx, *key
CompilerElse
MOV rdx, *key
CompilerEndIf
!mov eax, 2166136261
!jmp fnv32s_entry
!fnv32s_loop:
!xor eax, ecx
!imul eax, 0x01000193
CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
CompilerIf #PB_Compiler_Unicode
!add edx, 2
!fnv32s_entry:
!movzx ecx, word [edx]
CompilerElse
!add edx, 1
!fnv32s_entry:
!movzx ecx, byte [edx]
CompilerEndIf
CompilerElse
CompilerIf #PB_Compiler_Unicode
!add rdx, 2
!fnv32s_entry:
!movzx ecx, word [rdx]
CompilerElse
!add rdx, 1
!fnv32s_entry:
!movzx ecx, byte [rdx]
CompilerEndIf
CompilerEndIf
!cmp ecx, 0
!jne fnv32s_loop
DisableASM
ProcedureReturn
EndProcedure
A speed comparison of the FNV32 code with the 'push' fix added by Thorium, the modified version I made and MurmurHash3.
Code:
s.s = "The quick brown fox jumps over the lazy dog"
s_len.l = Len(s)
cycles.l = 10000000
cycles - 1
tm1 = ElapsedMilliseconds()
For i = 0 To cycles
a.l = FNV32(@s, s_len)
Next
tm2 = ElapsedMilliseconds()
For i = 0 To cycles
a.l = FNV32a(@s, s_len)
Next
tm3 = ElapsedMilliseconds()
For i = 0 To cycles
a.l = MurmurHash3(@s, s_len)
Next
tm4 = ElapsedMilliseconds()
result.s = "FNV32 : " + Str(tm2 - tm1) + Chr(10)
result + "FNV32a : " + Str(tm3 - tm2) + Chr(10)
result + "MurmurHash3 : " + Str(tm4 - tm3)
MessageRequester("Result", result)
The results of this test on my computer :
FNV32 : 796
FNV32a : 499
MurmurHash3 : 280