Schnelles CRC32 mit PCLMULQDQ

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
GPI
Beiträge: 1511
Registriert: 29.08.2004 13:18
Kontaktdaten:

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von GPI »

Tschuldigung, das ich das jetzt hochpushe, aber nachdem ja PB in 5.4 CRC32 nur mittels Stringumwege berechnen kann, wäre eine wirklich schnelle Routine wirklich toll...

Leider funktioniert hier irgendwie gar nichts mehr. Die Procedure CRC32_CL(Mem, Laenge) lässt sich nicht mehr in PB umsetzen.

Code: Alles auswählen

 CRC32Fingerprint(Mem + Laenge - (Laenge & $7F), Laenge & $7F, CRC32)   ;könnte auch noch in ASM erledigt werden
CRC32Fingerprint gibt es ja nicht mehr und genau diesen Speziellen Fall, wo man einen Startwert angibt, ist nicht mehr möglich!

Wenn man die Procedure rausnimmt, gibts Assembler-Fehler und das übersteigt meine Kenntnisse.

p.s.: Wer zahlt jetzt wenn die 100€? :)
CodeArchiv Rebirth: Deutsches Forum Github Hilfe ist immer gern gesehen!
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von Helle »

Huch, das mit PB-CRC32-Fingerprint in 5.40 ist mir garnicht bewusst gewesen. Muss ein Komplott Fred-Tommy sein; wollen sich wohl die 100€ teilen :bounce: ! Ich halte mich für unschuldig... :mrgreen:
Auf die Schnelle hier die Anpassung ohne PB:

Code: Alles auswählen

;- Windows7/64, PureBasic 5.40 (x64)
;- "Helle" Klaus Helbing, 02.12.2015
;- CRC32 mit PCLMULQDQ ohne PB-CRC32

Global.l CRC32

Procedure.l CRC32(Mem, Laenge, Table)
  !mov r8,[p.v_Table]                  ;lass ich hier mal so...
  !mov rdx,[p.v_Laenge]
  !mov r9,[p.v_Mem]
  !mov eax,[v_CRC32]
!@@:
  !movzx rcx,byte[r9]
  !xor cl,al
  !shr rax,8
  !xor eax,dword[r8+rcx*4]
  !inc r9
  !dec rdx
  !jnz @b
  !not eax                             ;wegen Polynomial Reversed
  ProcedureReturn
EndProcedure 

Procedure.l CRC32_CL(Mem, Laenge)
  ;Test auf PCLMULQDQ
  !MOV eax,1
  !CPUID
  !TEST ecx,2
  !JNZ CL_OK                           ;kann losgehen!
  !mov [v_CRC32],0FFFFFFFFh            ;Startwert für "normal"-ASM
  CRC32 = CRC32(Mem, Laenge, ?CRC_Table)
 ProcedureReturn CRC32

!CL_OK:
  !MOV [v_CRC32],0                     ;falls Laenge < 64
  !MOV r9,[p.v_Laenge]
  !AND r9,0FFFFFFFFFFFFFFC0h
!JZ less_64

  !MOV eax,0FFFFFFFFh                  ;Initialisierungswert für CRC32
  !MOVD xmm0,eax

  !MOV r8,[p.v_Mem]
  !MOVDQA xmm1,[r8]
  !MOVDQA xmm2,[r8+10h]
  !MOVDQA xmm3,[r8+20h]
  !MOVDQA xmm4,[r8+30h]
  !PXOR xmm1,xmm0

  !SUB r9,40h
  !ADD r8,40h

  !MOVDQA xmm0,[Lconstant_R2R1]

!loop_64:
  !PREFETCHNTA [r8+0c0h]              ;Cache "vorfüllen"
  !MOVDQA xmm5,xmm1
  !MOVDQA xmm6,xmm2
  !MOVDQA xmm7,xmm3
  !MOVDQA xmm8,xmm4

  !PCLMULQDQ xmm1,xmm0,00h
  !PCLMULQDQ xmm2,xmm0,00h
  !PCLMULQDQ xmm3,xmm0,00h
  !PCLMULQDQ xmm4,xmm0,00h

  !PCLMULQDQ xmm5,xmm0,11h
  !PCLMULQDQ xmm6,xmm0,11h
  !PCLMULQDQ xmm7,xmm0,11h
  !PCLMULQDQ xmm8,xmm0,11h

  !PXOR xmm1,xmm5
  !PXOR xmm2,xmm6
  !PXOR xmm3,xmm7
  !PXOR xmm4,xmm8

  !PXOR xmm1,[r8]
  !PXOR xmm2,[r8+10h]
  !PXOR xmm3,[r8+20h]
  !PXOR xmm4,[r8+30h]

  !SUB r9,40h
  !ADD r8,40h
  !CMP r9,40h
!JGE loop_64

  !MOVDQA xmm0,[Lconstant_R4R3]
  !PREFETCHNTA [r8]

  !MOVDQA xmm5,xmm1
  !PCLMULQDQ xmm1,xmm0,00h
  !PCLMULQDQ xmm5,xmm0,11h
  !PXOR xmm1,xmm5
  !PXOR xmm1,xmm2

  !MOVDQA xmm5,xmm1
  !PCLMULQDQ xmm1,xmm0,00h
  !PCLMULQDQ xmm5,xmm0,11h
  !PXOR xmm1,xmm5
  !PXOR xmm1,xmm3

  !MOVDQA xmm5,xmm1
  !PCLMULQDQ xmm1,xmm0,00h
  !PCLMULQDQ xmm5,xmm0,11h
  !PXOR xmm1,xmm5
  !PXOR xmm1,xmm4

  !PCLMULQDQ xmm0,xmm1,01h
  !PSRLDQ xmm1,08h
  !PXOR xmm1,xmm0

  !MOVDQA xmm2,xmm1

  !MOVDQA xmm0,[Lconstant_R5]
  !MOVDQA xmm3,[Lconstant_mask32]

  !PSRLDQ xmm2,04h
  !PAND xmm1,xmm3
  !PCLMULQDQ xmm1,xmm0,00h
  !PXOR xmm1,xmm2

  !MOVDQA xmm0,[Lconstant_RUpoly]

  !MOVDQA xmm2,xmm1
  !PAND xmm1,xmm3
  !PCLMULQDQ xmm1,xmm0,10h
  !PAND xmm1,xmm3
  !PCLMULQDQ xmm1,xmm0,00h
  !PXOR xmm1,xmm2
  !PEXTRD eax,xmm1,01h

  !MOV [v_CRC32],eax                   ;ist Startwert für Fortsetzung (falls Rest)

!less_64:                              ;Rest (max.63 Bytes)

  If Laenge & $3F
    CRC32 = CRC32(Mem + Laenge - (Laenge & $3F), Laenge & $3F, ?CRC_Table)
   Else
    CRC32 = ~CRC32                     ;NOT wegen Polynomial Reversed
  EndIf

ProcedureReturn CRC32

;Konstanten
!Align 16
!Lconstant_R2R1:
  !dq 0000000154442bd4h
  !dq 00000001c6e41596h
!Lconstant_R4R3:
  !dq 00000001751997d0h
  !dq 00000000ccaa009eh
!Lconstant_R5:
  !dq 0000000163cd6124h
  !dq 0000000000000000h
!Lconstant_mask32:
  !dq 00000000FFFFFFFFh
  !dq 0000000000000000h
!Lconstant_RUpoly:
  !dq 00000001DB710641h
  !dq 00000001F7011641h

EndProcedure

;Test
A$ = "The quick brown fox jumps over the lazy dog."
LA = Len(A$)
Faktor = 9999999
Buffer = AllocateMemory(LA * (Faktor + 1) + 16)

If Buffer
  BufferA = Buffer
  If BufferA & $0F                     ;muss Alignment 16 sein!
    BufferA = Buffer + 16 - (BufferA & $0F)
  EndIf

  For i = 0 To Faktor
    PokeS(BufferA + (i * LA), A$)
  Next
  Length = LA * (Faktor + 1)

  Time_ASM_A = ElapsedMilliseconds()
  CRC32 = $FFFFFFFF                              ;hier für Test mal so 
  CRC32 = CRC32(BufferA, Length, ?CRC_Table)     ;Achtung, bei > 4GB splitten!
  Time_ASM_E = ElapsedMilliseconds() - Time_ASM_A
  ASM$ = "CRC32_ASM = " + Hex(CRC32 & $FFFFFFFF) + "  in " + Str(Time_ASM_E) + " ms"

  Time_CL_A = ElapsedMilliseconds()
  CRC32 = CRC32_CL(BufferA, Length)
  Time_CL_E = ElapsedMilliseconds() - Time_CL_A
  CL$ = "CRC32_CL = " + Hex(CRC32 & $FFFFFFFF) + "  in " + Str(Time_CL_E) + " ms"

  FreeMemory(Buffer)
  MessageRequester("CRC32 ohne PB für " + Str(Length) + " Bytes", ASM$ + #LFCR$ + CL$)
EndIf

;für "normal"-ASM oder Tabelle während der Laufzeit erstellen
DataSection 
CRC_Table: 
  Data.l $0
  Data.l $77073096, $EE0E612C, $990951BA, $076DC419, $706AF48F 
  Data.l $E963A535, $9E6495A3, $0EDB8832, $79DCB8A4, $E0D5E91E 
  Data.l $97D2D988, $09B64C2B, $7EB17CBD, $E7B82D07, $90BF1D91 
  Data.l $1DB71064, $6AB020F2, $F3B97148, $84BE41DE, $1ADAD47D 
  Data.l $6DDDE4EB, $F4D4B551, $83D385C7, $136C9856, $646BA8C0 
  Data.l $FD62F97A, $8A65C9EC, $14015C4F, $63066CD9, $FA0F3D63 
  Data.l $8D080DF5, $3B6E20C8, $4C69105E, $D56041E4, $A2677172 
  Data.l $3C03E4D1, $4B04D447, $D20D85FD, $A50AB56B, $35B5A8FA 
  Data.l $42B2986C, $DBBBC9D6, $ACBCF940, $32D86CE3, $45DF5C75 
  Data.l $DCD60DCF, $ABD13D59, $26D930AC, $51DE003A, $C8D75180 
  Data.l $BFD06116, $21B4F4B5, $56B3C423, $CFBA9599, $B8BDA50F 
  Data.l $2802B89E, $5F058808, $C60CD9B2, $B10BE924, $2F6F7C87 
  Data.l $58684C11, $C1611DAB, $B6662D3D, $76DC4190, $01DB7106 
  Data.l $98D220BC, $EFD5102A, $71B18589, $06B6B51F, $9FBFE4A5 
  Data.l $E8B8D433, $7807C9A2, $0F00F934, $9609A88E, $E10E9818 
  Data.l $7F6A0DBB, $086D3D2D, $91646C97, $E6635C01, $6B6B51F4 
  Data.l $1C6C6162, $856530D8, $F262004E, $6C0695ED, $1B01A57B 
  Data.l $8208F4C1, $F50FC457, $65B0D9C6, $12B7E950, $8BBEB8EA 
  Data.l $FCB9887C, $62DD1DDF, $15DA2D49, $8CD37CF3, $FBD44C65 
  Data.l $4DB26158, $3AB551CE, $A3BC0074, $D4BB30E2, $4ADFA541 
  Data.l $3DD895D7, $A4D1C46D, $D3D6F4FB, $4369E96A, $346ED9FC 
  Data.l $AD678846, $DA60B8D0, $44042D73, $33031DE5, $AA0A4C5F 
  Data.l $DD0D7CC9, $5005713C, $270241AA, $BE0B1010, $C90C2086 
  Data.l $5768B525, $206F85B3, $B966D409, $CE61E49F, $5EDEF90E 
  Data.l $29D9C998, $B0D09822, $C7D7A8B4, $59B33D17, $2EB40D81 
  Data.l $B7BD5C3B, $C0BA6CAD, $EDB88320, $9ABFB3B6, $03B6E20C 
  Data.l $74B1D29A, $EAD54739, $9DD277AF, $04DB2615, $73DC1683 
  Data.l $E3630B12, $94643B84, $0D6D6A3E, $7A6A5AA8, $E40ECF0B 
  Data.l $9309FF9D, $0A00AE27, $7D079EB1, $F00F9344, $8708A3D2 
  Data.l $1E01F268, $6906C2FE, $F762575D, $806567CB, $196C3671 
  Data.l $6E6B06E7, $FED41B76, $89D32BE0, $10DA7A5A, $67DD4ACC 
  Data.l $F9B9DF6F, $8EBEEFF9, $17B7BE43, $60B08ED5, $D6D6A3E8 
  Data.l $A1D1937E, $38D8C2C4, $4FDFF252, $D1BB67F1, $A6BC5767 
  Data.l $3FB506DD, $48B2364B, $D80D2BDA, $AF0A1B4C, $36034AF6 
  Data.l $41047A60, $DF60EFC3, $A867DF55, $316E8EEF, $4669BE79 
  Data.l $CB61B38C, $BC66831A, $256FD2A0, $5268E236, $CC0C7795 
  Data.l $BB0B4703, $220216B9, $5505262F, $C5BA3BBE, $B2BD0B28 
  Data.l $2BB45A92, $5CB36A04, $C2D7FFA7, $B5D0CF31, $2CD99E8B 
  Data.l $5BDEAE1D, $9B64C2B0, $EC63F226, $756AA39C, $026D930A 
  Data.l $9C0906A9, $EB0E363F, $72076785, $05005713, $95BF4A82 
  Data.l $E2B87A14, $7BB12BAE, $0CB61B38, $92D28E9B, $E5D5BE0D 
  Data.l $7CDCEFB7, $0BDBDF21, $86D3D2D4, $F1D4E242, $68DDB3F8 
  Data.l $1FDA836E, $81BE16CD, $F6B9265B, $6FB077E1, $18B74777 
  Data.l $88085AE6, $FF0F6A70, $66063BCA, $11010B5C, $8F659EFF 
  Data.l $F862AE69, $616BFFD3, $166CCF45, $A00AE278, $D70DD2EE 
  Data.l $4E048354, $3903B3C2, $A7672661, $D06016F7, $4969474D 
  Data.l $3E6E77DB, $AED16A4A, $D9D65ADC, $40DF0B66, $37D83BF0 
  Data.l $A9BCAE53, $DEBB9EC5, $47B2CF7F, $30B5FFE9, $BDBDF21C 
  Data.l $CABAC28A, $53B39330, $24B4A3A6, $BAD03605, $CDD70693 
  Data.l $54DE5729, $23D967BF, $B3667A2E, $C4614AB8, $5D681B02 
  Data.l $2A6F2B94, $B40BBE37, $C30C8EA1, $5A05DF1B, $2D02EF8D 
EndDataSection 
Braucht bei mir (i7-6700K@4.7GHz) 24ms.
Gruß
Helle
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von Helle »

Zur Feier des Tages das Ganze als eine Prozedur:

Code: Alles auswählen

;- Windows7/64, PureBasic 5.40 LTS (x64)
;- "Helle" Klaus Helbing, 05.12.2015
;- CRC32 mit PCLMULQDQ ohne PB-CRC32, für 64-Bit-Windows
;- Basierend auf: http://stuff.mit.edu/afs/sipb/contrib/linux/arch/x86/crypto/crc32-pclmul_asm.S
;- Lizenz beachten!

Procedure.l CRC32_CL(Mem, Laenge)
  !MOV r9,[p.v_Laenge]
  !OR r9,r9                            ;erstmal Test, ob Länge = 0
 !JNZ Laenge_OK                        ;nicht 0
  !MOV rax,r9  
 ProcedureReturn                       ;Rückgabewert = 0 für Länge = 0

!Laenge_OK:
  ;Test auf PCLMULQDQ
  !MOV eax,1
  !CPUID
  !MOV eax,0FFFFFFFFh                  ;Initialisierungswert für CRC32
  !TEST ecx,2
 !JNZ CL_OK                            ;kann losgehen! Auskommentieren für Test ohne PCLMULQDQ

  !LEA r10,[CRC_Table]                 ;die CPU bringts nicht, also konventionell
  !MOV r8,[p.v_Mem]
!@@:
  !MOVZX rcx,byte[r8]
  !XOR cl,al
  !SHR rax,8
  !XOR eax,dword[r10+rcx*4]
  !INC r8
  !DEC r9                              ;Länge 
 !JNZ @b
  !NOT eax                             ;wegen Polynomial Reversed
 ProcedureReturn

!CL_OK:
  !AND r9,0FFFFFFFFFFFFFFC0h           ;Länge < 40?
 !JZ less_64                           ;ja, konventionell weiter
 
  !MOVD xmm0,eax                       ;eax = 0FFFFFFFFh  Initialisierungswert für CRC32
  !MOV r8,[p.v_Mem]
  !MOVDQA xmm1,[r8]
  !MOVDQA xmm2,[r8+10h]
  !MOVDQA xmm3,[r8+20h]
  !MOVDQA xmm4,[r8+30h]
  !PXOR xmm1,xmm0

  !SUB r9,40h
  !ADD r8,40h

  !MOVDQA xmm0,[Lconstant_R2R1]

!loop_64:
  !PREFETCHNTA [r8+0c0h]               ;Cache "vorfüllen"
  !MOVDQA xmm5,xmm1
  !MOVDQA xmm6,xmm2
  !MOVDQA xmm7,xmm3
  !MOVDQA xmm8,xmm4

  !PCLMULQDQ xmm1,xmm0,00h
  !PCLMULQDQ xmm2,xmm0,00h
  !PCLMULQDQ xmm3,xmm0,00h
  !PCLMULQDQ xmm4,xmm0,00h

  !PCLMULQDQ xmm5,xmm0,11h
  !PCLMULQDQ xmm6,xmm0,11h
  !PCLMULQDQ xmm7,xmm0,11h
  !PCLMULQDQ xmm8,xmm0,11h

  !PXOR xmm1,xmm5
  !PXOR xmm2,xmm6
  !PXOR xmm3,xmm7
  !PXOR xmm4,xmm8

  !PXOR xmm1,[r8]
  !PXOR xmm2,[r8+10h]
  !PXOR xmm3,[r8+20h]
  !PXOR xmm4,[r8+30h]

  !SUB r9,40h
  !ADD r8,40h
  !CMP r9,40h
 !JGE loop_64

  !MOVDQA xmm0,[Lconstant_R4R3]
  !PREFETCHNTA [r8]

  !MOVDQA xmm5,xmm1
  !PCLMULQDQ xmm1,xmm0,00h
  !PCLMULQDQ xmm5,xmm0,11h
  !PXOR xmm1,xmm5
  !PXOR xmm1,xmm2

  !MOVDQA xmm5,xmm1
  !PCLMULQDQ xmm1,xmm0,00h
  !PCLMULQDQ xmm5,xmm0,11h
  !PXOR xmm1,xmm5
  !PXOR xmm1,xmm3

  !MOVDQA xmm5,xmm1
  !PCLMULQDQ xmm1,xmm0,00h
  !PCLMULQDQ xmm5,xmm0,11h
  !PXOR xmm1,xmm5
  !PXOR xmm1,xmm4

  !PCLMULQDQ xmm0,xmm1,01h
  !PSRLDQ xmm1,08h
  !PXOR xmm1,xmm0

  !MOVDQA xmm2,xmm1

  !MOVDQA xmm0,[Lconstant_R5]
  !MOVDQA xmm3,[Lconstant_mask32]

  !PSRLDQ xmm2,04h
  !PAND xmm1,xmm3
  !PCLMULQDQ xmm1,xmm0,00h
  !PXOR xmm1,xmm2

  !MOVDQA xmm0,[Lconstant_RUpoly]

  !MOVDQA xmm2,xmm1
  !PAND xmm1,xmm3
  !PCLMULQDQ xmm1,xmm0,10h
  !PAND xmm1,xmm3
  !PCLMULQDQ xmm1,xmm0,00h
  !PXOR xmm1,xmm2
  !PEXTRD eax,xmm1,01h                 ;eax ist CRC32 (bis hierher)
!less_64:                              ;Rest (max.63 Bytes)

  !MOV r8,[p.v_Mem]
  !MOV r9,[p.v_Laenge]
  !ADD r8,r9
  !AND r9,3fh                          ;möglicher Rest
 !JZ NoRest                            ;kein Rest
  !LEA r10,[CRC_Table]                 ;die restlichen Bytes mit Tabelle
  !SUB r8,r9
!@@:
  !MOVZX rcx,byte[r8]
  !XOR cl,al
  !SHR rax,8
  !XOR eax,dword[r10+rcx*4]
  !INC r8
  !DEC r9
 !JNZ @b
!NoRest:
  !NOT eax                             ;wegen Polynomial Reversed
 ProcedureReturn

;Konstanten
!Align 16
!Lconstant_R2R1:
  !dq 0000000154442BD4h, 00000001C6E41596h
!Lconstant_R4R3:
  !dq 00000001751997D0h, 00000000CCAA009Eh
!Lconstant_R5:
  !dq 0000000163CD6124h, 0000000000000000h
!Lconstant_mask32:
  !dq 00000000FFFFFFFFh, 0000000000000000h
!Lconstant_RUpoly:
  !dq 00000001DB710641h, 00000001F7011641h

!CRC_Table:
  !dd 0
  !dd 077073096h, 0EE0E612Ch, 0990951BAh, 0076DC419h, 0706AF48Fh
  !dd 0E963A535h, 09E6495A3h, 00EDB8832h, 079DCB8A4h, 0E0D5E91Eh
  !dd 097D2D988h, 009B64C2Bh, 07EB17CBDh, 0E7B82D07h, 090BF1D91h
  !dd 01DB71064h, 06AB020F2h, 0F3B97148h, 084BE41DEh, 01ADAD47Dh
  !dd 06DDDE4EBh, 0F4D4B551h, 083D385C7h, 0136C9856h, 0646BA8C0h
  !dd 0FD62F97Ah, 08A65C9ECh, 014015C4Fh, 063066CD9h, 0FA0F3D63h 
  !dd 08D080DF5h, 03B6E20C8h, 04C69105Eh, 0D56041E4h, 0A2677172h
  !dd 03C03E4D1h, 04B04D447h, 0D20D85FDh, 0A50AB56Bh, 035B5A8FAh
  !dd 042B2986Ch, 0DBBBC9D6h, 0ACBCF940h, 032D86CE3h, 045DF5C75h
  !dd 0DCD60DCFh, 0ABD13D59h, 026D930ACh, 051DE003Ah, 0C8D75180h
  !dd 0BFD06116h, 021B4F4B5h, 056B3C423h, 0CFBA9599h, 0B8BDA50Fh
  !dd 02802B89Eh, 05F058808h, 0C60CD9B2h, 0B10BE924h, 02F6F7C87h
  !dd 058684C11h, 0C1611DABh, 0B6662D3Dh, 076DC4190h, 001DB7106h
  !dd 098D220BCh, 0EFD5102Ah, 071B18589h, 006B6B51Fh, 09FBFE4A5h
  !dd 0E8B8D433h, 07807C9A2h, 00F00F934h, 09609A88Eh, 0E10E9818h
  !dd 07F6A0DBBh, 0086D3D2Dh, 091646C97h, 0E6635C01h, 06B6B51F4h
  !dd 01C6C6162h, 0856530D8h, 0F262004Eh, 06C0695EDh, 01B01A57Bh
  !dd 08208F4C1h, 0F50FC457h, 065B0D9C6h, 012B7E950h, 08BBEB8EAh
  !dd 0FCB9887Ch, 062DD1DDFh, 015DA2D49h, 08CD37CF3h, 0FBD44C65h
  !dd 04DB26158h, 03AB551CEh, 0A3BC0074h, 0D4BB30E2h, 04ADFA541h
  !dd 03DD895D7h, 0A4D1C46Dh, 0D3D6F4FBh, 04369E96Ah, 0346ED9FCh
  !dd 0AD678846h, 0DA60B8D0h, 044042D73h, 033031DE5h, 0AA0A4C5Fh
  !dd 0DD0D7CC9h, 05005713Ch, 0270241AAh, 0BE0B1010h, 0C90C2086h
  !dd 05768B525h, 0206F85B3h, 0B966D409h, 0CE61E49Fh, 05EDEF90Eh
  !dd 029D9C998h, 0B0D09822h, 0C7D7A8B4h, 059B33D17h, 02EB40D81h
  !dd 0B7BD5C3Bh, 0C0BA6CADh, 0EDB88320h, 09ABFB3B6h, 003B6E20Ch
  !dd 074B1D29Ah, 0EAD54739h, 09DD277AFh, 004DB2615h, 073DC1683h
  !dd 0E3630B12h, 094643B84h, 00D6D6A3Eh, 07A6A5AA8h, 0E40ECF0Bh
  !dd 09309FF9Dh, 00A00AE27h, 07D079EB1h, 0F00F9344h, 08708A3D2h
  !dd 01E01F268h, 06906C2FEh, 0F762575Dh, 0806567CBh, 0196C3671h
  !dd 06E6B06E7h, 0FED41B76h, 089D32BE0h, 010DA7A5Ah, 067DD4ACCh
  !dd 0F9B9DF6Fh, 08EBEEFF9h, 017B7BE43h, 060B08ED5h, 0D6D6A3E8h
  !dd 0A1D1937Eh, 038D8C2C4h, 04FDFF252h, 0D1BB67F1h, 0A6BC5767h
  !dd 03FB506DDh, 048B2364Bh, 0D80D2BDAh, 0AF0A1B4Ch, 036034AF6h
  !dd 041047A60h, 0DF60EFC3h, 0A867DF55h, 0316E8EEFh, 04669BE79h
  !dd 0CB61B38Ch, 0BC66831Ah, 0256FD2A0h, 05268E236h, 0CC0C7795h
  !dd 0BB0B4703h, 0220216B9h, 05505262Fh, 0C5BA3BBEh, 0B2BD0B28h
  !dd 02BB45A92h, 05CB36A04h, 0C2D7FFA7h, 0B5D0CF31h, 02CD99E8Bh
  !dd 05BDEAE1Dh, 09B64C2B0h, 0EC63F226h, 0756AA39Ch, 0026D930Ah
  !dd 09C0906A9h, 0EB0E363Fh, 072076785h, 005005713h, 095BF4A82h
  !dd 0E2B87A14h, 07BB12BAEh, 00CB61B38h, 092D28E9Bh, 0E5D5BE0Dh
  !dd 07CDCEFB7h, 00BDBDF21h, 086D3D2D4h, 0F1D4E242h, 068DDB3F8h
  !dd 01FDA836Eh, 081BE16CDh, 0F6B9265Bh, 06FB077E1h, 018B74777h
  !dd 088085AE6h, 0FF0F6A70h, 066063BCAh, 011010B5Ch, 08F659EFFh
  !dd 0F862AE69h, 0616BFFD3h, 0166CCF45h, 0A00AE278h, 0D70DD2EEh
  !dd 04E048354h, 03903B3C2h, 0A7672661h, 0D06016F7h, 04969474Dh
  !dd 03E6E77DBh, 0AED16A4Ah, 0D9D65ADCh, 040DF0B66h, 037D83BF0h
  !dd 0A9BCAE53h, 0DEBB9EC5h, 047B2CF7Fh, 030B5FFE9h, 0BDBDF21Ch
  !dd 0CABAC28Ah, 053B39330h, 024B4A3A6h, 0BAD03605h, 0CDD70693h
  !dd 054DE5729h, 023D967BFh, 0B3667A2Eh, 0C4614AB8h, 05D681B02h
  !dd 02A6F2B94h, 0B40BBE37h, 0C30C8EA1h, 05A05DF1Bh, 02D02EF8Dh
EndProcedure

;Test
A$ = "The quick brown fox jumps over the lazy dog."
LA = Len(A$)
Faktor = 9999999
Buffer = AllocateMemory(LA * (Faktor + 1) + 16)

If Buffer
  BufferA = Buffer
  If BufferA & $0F                     ;muss Alignment 16 sein!
    BufferA = Buffer + 16 - (BufferA & $0F)
  EndIf

  For i = 0 To Faktor
    PokeS(BufferA + (i * LA), A$)
  Next
  Length = LA * (Faktor + 1)

  Time_CL_A = ElapsedMilliseconds()
  CRC32 = CRC32_CL(BufferA, Length)              ;5FCF543F für obige Werte
  Time_CL_E = ElapsedMilliseconds() - Time_CL_A  ;24ms für i7-6700K@4.7GHz und DDR4/2400
  CL$ = "CRC32_CL = " + Hex(CRC32 & $FFFFFFFF) + "  in " + Str(Time_CL_E) + " ms"

  FreeMemory(Buffer)
  MessageRequester("CRC32 ohne PB für " + Str(Length) + " Bytes", CL$)
EndIf
Gruß
Helle
GPI
Beiträge: 1511
Registriert: 29.08.2004 13:18
Kontaktdaten:

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von GPI »

Ich würde eine kleine Verbesserung machen:
aus
LA = Len(A$)
wird
LA = Len(A$)*SizeOf(character)

Damit dürfte es dann fehlerfrei mit Unicode funktionieren.

Wird in CodeArchiv unter Cipher\Fast_CRC32 [64BIT].pbi auftauchen :)
CodeArchiv Rebirth: Deutsches Forum Github Hilfe ist immer gern gesehen!
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von Helle »

Da ich sehe, das auch eine 32-Bit-Version für Windows von Interesse sein könnte, hier der schmucklose und nicht optimierte 32-Bit-Code:

Code: Alles auswählen

;- PureBasic 5.40 LTS (x86)
;- "Helle" Klaus Helbing, 11.12.2015
;- CRC32 mit PCLMULQDQ ohne PB-CRC32, für 32-Bit-Windows
;- Basierend auf: http://stuff.mit.edu/afs/sipb/contrib/linux/arch/x86/crypto/crc32-pclmul_asm.S
;- Lizenz beachten!

Procedure.l CRC32_CL(Mem, Laenge)
  !MOV eax,[p.v_Laenge]
  !OR eax,eax                          ;erstmal Test, ob Länge = 0
 !JNZ Laenge_OK                        ;nicht 0
 ProcedureReturn                       ;Rückgabewert = 0 für Länge = 0

!Laenge_OK:
  ;Test auf PCLMULQDQ
  !MOV eax,1
  !CPUID
  !MOV eax,0FFFFFFFFh                  ;Initialisierungswert für CRC32
 
  !TEST ecx,2
 !JNZ CL_OK                            ;kann losgehen! Auskommentieren für Test ohne PCLMULQDQ

  !LEA ecx,[CRC_Table]                 ;die CPU bringts nicht, also konventionell
  !MOV eax,[p.v_Mem]
  !MOV edx,[p.v_Laenge]
  !PUSH ebx
  !MOV ebx,ecx
  !PUSH esi
  !MOV esi,eax
  !MOV eax,0FFFFFFFFh                  ;Initialisierungswert für CRC32 
!@@:
  !MOVZX ecx,byte[esi]
  !XOR cl,al
  !SHR eax,8
  !XOR eax,dword[ebx+ecx*4]
  !INC esi
  !DEC edx                             ;Länge 
 !JNZ @b
 
  !POP esi
  !POP ebx
  !NOT eax                             ;wegen Polynomial Reversed
 ProcedureReturn

!CL_OK:
  !MOV eax,0FFFFFFFFh                  ;Initialisierungswert für CRC32
  !MOV edx,[p.v_Laenge]
  !AND edx,0FFFFFFC0h                  ;Länge < 40h?
 !JZ less_64                           ;ja, konventionell weiter
 
  !MOVD xmm5,eax                       ;eax = 0FFFFFFFFh  Initialisierungswert für CRC32
  !MOV ecx,[p.v_Mem]
  !MOVDQA xmm0,[ecx]
  !MOVDQA xmm1,[ecx+10h]
  !MOVDQA xmm2,[ecx+20h]
  !MOVDQA xmm3,[ecx+30h]
  !PXOR xmm0,xmm5

  !SUB edx,40h
  !ADD ecx,40h
 
!loop_64:
  !PREFETCHNTA [ecx+0c0h]              ;Cache "vorfüllen"

  !MOVDQA xmm4,xmm0
  !MOVDQA xmm5,xmm1
  !MOVDQA xmm6,xmm2
  !MOVDQA xmm7,xmm3

  !PCLMULQDQ xmm0,[Lconstant_R2R1],00h
  !PCLMULQDQ xmm1,[Lconstant_R2R1],00h
  !PCLMULQDQ xmm2,[Lconstant_R2R1],00h
  !PCLMULQDQ xmm3,[Lconstant_R2R1],00h

  !PCLMULQDQ xmm4,[Lconstant_R2R1],11h
  !PCLMULQDQ xmm5,[Lconstant_R2R1],11h
  !PCLMULQDQ xmm6,[Lconstant_R2R1],11h
  !PCLMULQDQ xmm7,[Lconstant_R2R1],11h

  !PXOR xmm0,xmm4
  !PXOR xmm1,xmm5
  !PXOR xmm2,xmm6
  !PXOR xmm3,xmm7

  !PXOR xmm0,[ecx]
  !PXOR xmm1,[ecx+10h]
  !PXOR xmm2,[ecx+20h]
  !PXOR xmm3,[ecx+30h]

  !SUB edx,40h
  !ADD ecx,40h
  !CMP edx,40h
 !JGE loop_64

  !MOVDQA xmm6,[Lconstant_R4R3]
  !PREFETCHNTA [ecx]

  !MOVDQA xmm4,xmm0
  !PCLMULQDQ xmm0,xmm6,00h
  !PCLMULQDQ xmm4,xmm6,11h
  !PXOR xmm0,xmm4
  !PXOR xmm0,xmm1

  !MOVDQA xmm4,xmm0
  !PCLMULQDQ xmm0,xmm6,00h
  !PCLMULQDQ xmm4,xmm6,11h
  !PXOR xmm0,xmm4
  !PXOR xmm0,xmm2

  !MOVDQA xmm4,xmm0
  !PCLMULQDQ xmm0,xmm6,00h
  !PCLMULQDQ xmm4,xmm6,11h
  !PXOR xmm0,xmm4
  !PXOR xmm0,xmm3

  !PCLMULQDQ xmm6,xmm0,01h
  !PSRLDQ xmm0,08h
  !PXOR xmm0,xmm6

  !MOVDQA xmm1,xmm0

  !MOVDQA xmm6,[Lconstant_R5]
  !MOVDQA xmm2,[Lconstant_mask32]

  !PSRLDQ xmm1,04h
  !PAND xmm0,xmm2
  !PCLMULQDQ xmm0,xmm6,00h
  !PXOR xmm0,xmm1

  !MOVDQA xmm6,[Lconstant_RUpoly]

  !MOVDQA xmm1,xmm0
  !PAND xmm0,xmm2
  !PCLMULQDQ xmm0,xmm6,10h
  !PAND xmm0,xmm2
  !PCLMULQDQ xmm0,xmm6,00h
  !PXOR xmm0,xmm1

!less_64:                              ;Rest (max.63 Bytes)
  !LEA ecx,[CRC_Table]                 ;die restlichen Bytes mit Tabelle
  !MOV eax,[p.v_Mem]
  !MOV edx,[p.v_Laenge]
  !PUSH ebx
  !MOV ebx,ecx
  !PUSH esi
  !MOV esi,eax

  !PEXTRD eax,xmm0,01h                 ;eax ist CRC32 (bis hierher)

  !ADD esi,edx
  !AND edx,3fh                         ;möglicher Rest
 !JZ NoRest                            ;kein Rest
  !SUB esi,edx
!@@:
  !MOVZX ecx,byte[esi]
  !XOR cl,al
  !SHR eax,8
  !XOR eax,dword[ebx+ecx*4]
  !INC esi
  !DEC edx
 !JNZ @b
  
!NoRest:
  !POP esi
  !POP ebx
  !NOT eax                             ;wegen Polynomial Reversed
 ProcedureReturn

;Konstanten
!Align 16
!Lconstant_R2R1:
  !dq 0000000154442BD4h, 00000001C6E41596h
!Lconstant_R4R3:
  !dq 00000001751997D0h, 00000000CCAA009Eh
!Lconstant_R5:
  !dq 0000000163CD6124h, 0000000000000000h
!Lconstant_mask32:
  !dq 00000000FFFFFFFFh, 0000000000000000h
!Lconstant_RUpoly:
  !dq 00000001DB710641h, 00000001F7011641h

!CRC_Table:
  !dd 0
  !dd 077073096h, 0EE0E612Ch, 0990951BAh, 0076DC419h, 0706AF48Fh
  !dd 0E963A535h, 09E6495A3h, 00EDB8832h, 079DCB8A4h, 0E0D5E91Eh
  !dd 097D2D988h, 009B64C2Bh, 07EB17CBDh, 0E7B82D07h, 090BF1D91h
  !dd 01DB71064h, 06AB020F2h, 0F3B97148h, 084BE41DEh, 01ADAD47Dh
  !dd 06DDDE4EBh, 0F4D4B551h, 083D385C7h, 0136C9856h, 0646BA8C0h
  !dd 0FD62F97Ah, 08A65C9ECh, 014015C4Fh, 063066CD9h, 0FA0F3D63h 
  !dd 08D080DF5h, 03B6E20C8h, 04C69105Eh, 0D56041E4h, 0A2677172h
  !dd 03C03E4D1h, 04B04D447h, 0D20D85FDh, 0A50AB56Bh, 035B5A8FAh
  !dd 042B2986Ch, 0DBBBC9D6h, 0ACBCF940h, 032D86CE3h, 045DF5C75h
  !dd 0DCD60DCFh, 0ABD13D59h, 026D930ACh, 051DE003Ah, 0C8D75180h
  !dd 0BFD06116h, 021B4F4B5h, 056B3C423h, 0CFBA9599h, 0B8BDA50Fh
  !dd 02802B89Eh, 05F058808h, 0C60CD9B2h, 0B10BE924h, 02F6F7C87h
  !dd 058684C11h, 0C1611DABh, 0B6662D3Dh, 076DC4190h, 001DB7106h
  !dd 098D220BCh, 0EFD5102Ah, 071B18589h, 006B6B51Fh, 09FBFE4A5h
  !dd 0E8B8D433h, 07807C9A2h, 00F00F934h, 09609A88Eh, 0E10E9818h
  !dd 07F6A0DBBh, 0086D3D2Dh, 091646C97h, 0E6635C01h, 06B6B51F4h
  !dd 01C6C6162h, 0856530D8h, 0F262004Eh, 06C0695EDh, 01B01A57Bh
  !dd 08208F4C1h, 0F50FC457h, 065B0D9C6h, 012B7E950h, 08BBEB8EAh
  !dd 0FCB9887Ch, 062DD1DDFh, 015DA2D49h, 08CD37CF3h, 0FBD44C65h
  !dd 04DB26158h, 03AB551CEh, 0A3BC0074h, 0D4BB30E2h, 04ADFA541h
  !dd 03DD895D7h, 0A4D1C46Dh, 0D3D6F4FBh, 04369E96Ah, 0346ED9FCh
  !dd 0AD678846h, 0DA60B8D0h, 044042D73h, 033031DE5h, 0AA0A4C5Fh
  !dd 0DD0D7CC9h, 05005713Ch, 0270241AAh, 0BE0B1010h, 0C90C2086h
  !dd 05768B525h, 0206F85B3h, 0B966D409h, 0CE61E49Fh, 05EDEF90Eh
  !dd 029D9C998h, 0B0D09822h, 0C7D7A8B4h, 059B33D17h, 02EB40D81h
  !dd 0B7BD5C3Bh, 0C0BA6CADh, 0EDB88320h, 09ABFB3B6h, 003B6E20Ch
  !dd 074B1D29Ah, 0EAD54739h, 09DD277AFh, 004DB2615h, 073DC1683h
  !dd 0E3630B12h, 094643B84h, 00D6D6A3Eh, 07A6A5AA8h, 0E40ECF0Bh
  !dd 09309FF9Dh, 00A00AE27h, 07D079EB1h, 0F00F9344h, 08708A3D2h
  !dd 01E01F268h, 06906C2FEh, 0F762575Dh, 0806567CBh, 0196C3671h
  !dd 06E6B06E7h, 0FED41B76h, 089D32BE0h, 010DA7A5Ah, 067DD4ACCh
  !dd 0F9B9DF6Fh, 08EBEEFF9h, 017B7BE43h, 060B08ED5h, 0D6D6A3E8h
  !dd 0A1D1937Eh, 038D8C2C4h, 04FDFF252h, 0D1BB67F1h, 0A6BC5767h
  !dd 03FB506DDh, 048B2364Bh, 0D80D2BDAh, 0AF0A1B4Ch, 036034AF6h
  !dd 041047A60h, 0DF60EFC3h, 0A867DF55h, 0316E8EEFh, 04669BE79h
  !dd 0CB61B38Ch, 0BC66831Ah, 0256FD2A0h, 05268E236h, 0CC0C7795h
  !dd 0BB0B4703h, 0220216B9h, 05505262Fh, 0C5BA3BBEh, 0B2BD0B28h
  !dd 02BB45A92h, 05CB36A04h, 0C2D7FFA7h, 0B5D0CF31h, 02CD99E8Bh
  !dd 05BDEAE1Dh, 09B64C2B0h, 0EC63F226h, 0756AA39Ch, 0026D930Ah
  !dd 09C0906A9h, 0EB0E363Fh, 072076785h, 005005713h, 095BF4A82h
  !dd 0E2B87A14h, 07BB12BAEh, 00CB61B38h, 092D28E9Bh, 0E5D5BE0Dh
  !dd 07CDCEFB7h, 00BDBDF21h, 086D3D2D4h, 0F1D4E242h, 068DDB3F8h
  !dd 01FDA836Eh, 081BE16CDh, 0F6B9265Bh, 06FB077E1h, 018B74777h
  !dd 088085AE6h, 0FF0F6A70h, 066063BCAh, 011010B5Ch, 08F659EFFh
  !dd 0F862AE69h, 0616BFFD3h, 0166CCF45h, 0A00AE278h, 0D70DD2EEh
  !dd 04E048354h, 03903B3C2h, 0A7672661h, 0D06016F7h, 04969474Dh
  !dd 03E6E77DBh, 0AED16A4Ah, 0D9D65ADCh, 040DF0B66h, 037D83BF0h
  !dd 0A9BCAE53h, 0DEBB9EC5h, 047B2CF7Fh, 030B5FFE9h, 0BDBDF21Ch
  !dd 0CABAC28Ah, 053B39330h, 024B4A3A6h, 0BAD03605h, 0CDD70693h
  !dd 054DE5729h, 023D967BFh, 0B3667A2Eh, 0C4614AB8h, 05D681B02h
  !dd 02A6F2B94h, 0B40BBE37h, 0C30C8EA1h, 05A05DF1Bh, 02D02EF8Dh
EndProcedure

;Test
A$ = "The quick brown fox jumps over the lazy dog."
LA = Len(A$)
;LA = Len(A$)*SizeOf(character)         ;für Unicode
Faktor = 9999999
Buffer = AllocateMemory(LA * (Faktor + 1) + 16)

If Buffer
  BufferA = Buffer
  If BufferA & $0F                     ;muss Alignment 16 sein!
    BufferA = Buffer + 16 - (BufferA & $0F)
  EndIf

  For i = 0 To Faktor
    PokeS(BufferA + (i * LA), A$)
  Next
  Length = LA * (Faktor + 1)

  Time_CL_A = ElapsedMilliseconds()
  CRC32 = CRC32_CL(BufferA, Length)              ;5FCF543F für obige Werte
  Time_CL_E = ElapsedMilliseconds() - Time_CL_A 
  CL$ = "CRC32_CL = " + Hex(CRC32 & $FFFFFFFF) + "  in " + Str(Time_CL_E) + " ms"

  FreeMemory(Buffer)
  MessageRequester("CRC32 ohne PB für " + Str(Length) + " Bytes", CL$)
EndIf
Viel Spaß!
Helle
Benutzeravatar
TheCube
Beiträge: 150
Registriert: 20.07.2010 23:59
Computerausstattung: Risen 3400G 16MB Win10-64Bit
Wohnort: NRW

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von TheCube »

:allright: Prima Code-Alternative, und für PB32bit geeignet kann man seine Exen "überall" laufen lassen bei Bedarf.

Hier ein paar Messwerte:

Code: Alles auswählen

Helle CRC32 : i5-4430S 2,7Ghz Win7-64:   45ms   Atom 2-Kern E3827 1.75GHz Win7-64 :  355ms   Atom 330 2-Kern 2GHz Win7-32 (ohne PCLMULQDQ) : 1640ms
PB CRC32    : i5-4430S 2,7Ghz Win7-64: 1210ms   Atom 2-Kern E3827 1.75GHz Win7-64 : 1612ms   Atom 330 2-Kern 2GHz Win7-32 (ohne PCLMULQDQ) : 2120ms
GPI
Beiträge: 1511
Registriert: 29.08.2004 13:18
Kontaktdaten:

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von GPI »

Ich hab das ganze in einer Include-Datei zusammengefasst - die je nach einstellungen die 32 oder 64Bit version nimmt:
https://github.com/GPIforGit/PureBasic- ... _CRC32.pbi
CodeArchiv Rebirth: Deutsches Forum Github Hilfe ist immer gern gesehen!
topsoft
Beiträge: 67
Registriert: 16.09.2004 11:55
Wohnort: nrw

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von topsoft »

Hi,

super Code, aber ....
der geht leider nicht über mehrere Speicherbereiche. Braucht man aber wenn man eine Datei in Teilstücken lädt.

Ich meine sowas:

Code: Alles auswählen

Procedure.i FileCrc32(Filename.s)
  Protected Anzahl.i, Crc32.i = $FFFFFFFF, hFile.i = ReadFile(#PB_Any, Filename, #PB_File_SharedRead | #PB_File_SharedWrite)
  If hFile
    Protected *buffer = AllocateMemory($FFFF)
    If *buffer
      While Not Eof(hFile)
        Anzahl = ReadData(hFile, *buffer, $FFFF)
        Crc32 = crc32_cl(*buffer, Anzahl, Crc32)
      Wend
      FreeMemory(*buffer)
    EndIf
    CloseFile(hFile)
  EndIf
  ProcedureReturn Crc32
EndProcedure
Gruß Rene
Skylake QuadCore i7 6700K @4400MHz, MSI Z170A Gaming M5, 64 GB DDR4 @ 2133MHz, B: Ramdisk 32GB, C: Raid0 SATA SSD 1TB, D: Raid0 M2 SSD 1TB, E: Raid0 HDD 8TB, 28" 4K @ RTX2080, Win10 X64
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von Helle »

Ist doch auch kein Problem; die Procedure wird um einen dritten Parameter erweitert: CRC_Start. Knackpunkt ist die notwendige Invertierung des End-Ergebnisses, hier NOT EAX. Dies müsste überall entfernt werden und erst das endgültige Ergebnis dann invertiert werden; ist aber sehr unelegant. So sollte es praktikabel sein (Anfang der Procedure):

Code: Alles auswählen

Procedure.l CRC32_CL(Mem, Laenge, CRC_Start)     ;CRC_Start fängt hier mit Null an!
  !MOV r9,[p.v_Laenge]
  !OR r9,r9                            ;erstmal Test, ob Länge = 0
 !JNZ Laenge_OK                        ;nicht 0
  !MOV rax,r9  
 ProcedureReturn                       ;Rückgabewert = 0 für Länge = 0

!Laenge_OK:
  ;Test auf PCLMULQDQ
  !MOV eax,1
  !CPUID
  ;!MOV eax,0FFFFFFFFh                  ;Initialisierungswert für CRC32
  ;----- neu für 3-Parameter-Variante
  !MOV eax,[p.v_CRC_Start]             ;liest vom Stack, muss nicht rax sein 
  !NOT eax                             ;vlt. der einfachste Weg
  ;-----
  !TEST ecx,2
 !JNZ CL_OK                            ;kann losgehen! Auskommentieren für Test ohne PCLMULQDQ
...
...

Unbedingt beachten, das der Start-Wert für CRC_Start jetzt Null ist, nicht mehr $FFFFFFFF!
So kann die Procedure mit dem Ergebnis des letzten Teilstückes aufgerufen werden.

Probiers aus :D !
Gruß
Helle
topsoft
Beiträge: 67
Registriert: 16.09.2004 11:55
Wohnort: nrw

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von topsoft »

Hi, funzt prächtig. Vielen Dank an 'Helle'. Anbei noch mal der geänderte Code und die Procedure für ein CRC über eine Datei.
Ist vieleicht was für das Codearchiv?

Code: Alles auswählen

Procedure.i FileCrc32(Filename.s)
  Protected Anzahl.i, Crc32.i = 0, hFile.i = ReadFile(#PB_Any, Filename, #PB_File_SharedRead | #PB_File_SharedWrite)
  If hFile
    Protected *buffer = AllocateMemory($FFFF)
    If *buffer
      While Not Eof(hFile)
        Anzahl = ReadData(hFile, *buffer, $FFFF)
        Crc32 = crc32_cl(*buffer, Anzahl, Crc32)
      Wend
      FreeMemory(*buffer)
    EndIf
    CloseFile(hFile)
  EndIf
  ProcedureReturn Crc32
EndProcedure

Code: Alles auswählen

  ;- PureBasic 5.40 LTS (x86)
  ;- "Helle" Klaus Helbing, 11.12.2015
  ;- CRC32 mit PCLMULQDQ ohne PB-CRC32, für Windows
  ;- Basierend auf: http://stuff.mit.edu/afs/sipb/contrib/linux/arch/x86/crypto/crc32-pclmul_asm.S
  ;- Lizenz beachten!
  Procedure.l CRC32_CL(Mem, Laenge, Crc32)
  CompilerIf #PB_Compiler_Processor=#PB_Processor_x64    
    !MOV r9,[p.v_Laenge]
    !OR r9,r9                            ;erstmal Test, ob Länge = 0
    !JNZ Laenge_OK                        ;nicht 0
    !MOV rax,r9 
    ProcedureReturn                       ;Rückgabewert = 0 für Länge = 0

    !Laenge_OK:
    ;Test auf PCLMULQDQ
    !MOV eax,1
    !CPUID
    !MOV eax,[p.v_Crc32]             ;liest vom Stack, muss nicht rax sein
    !NOT eax                             ;vlt. der einfachste Weg
    ;-----
    !TEST ecx,2
    !JNZ CL_OK                            ;kann losgehen! Auskommentieren für Test ohne PCLMULQDQ
    
    !LEA r10,[CRC_Table]                 ;die CPU bringts nicht, also konventionell
    !MOV r8,[p.v_Mem]
    !@@:
    !MOVZX rcx,byte[r8]
    !XOR cl,al
    !SHR rax,8
    !XOR eax,dword[r10+rcx*4]
    !INC r8
    !DEC r9                              ;Länge
    !JNZ @b
    !NOT eax                             ;wegen Polynomial Reversed
    ProcedureReturn
    
    !CL_OK:
    !AND r9,0FFFFFFFFFFFFFFC0h           ;Länge < 40?
    !JZ less_64                           ;ja, konventionell weiter
    
    !MOVD xmm0,eax                       ;eax = 0FFFFFFFFh  Initialisierungswert für CRC32
    !MOV r8,[p.v_Mem]
    !MOVDQA xmm1,[r8]
    !MOVDQA xmm2,[r8+10h]
    !MOVDQA xmm3,[r8+20h]
    !MOVDQA xmm4,[r8+30h]
    !PXOR xmm1,xmm0
    
    !SUB r9,40h
    !ADD r8,40h
    
    !MOVDQA xmm0,[Lconstant_R2R1]
    
    !loop_64:
    !PREFETCHNTA [r8+0c0h]               ;Cache "vorfüllen"
    !MOVDQA xmm5,xmm1
    !MOVDQA xmm6,xmm2
    !MOVDQA xmm7,xmm3
    !MOVDQA xmm8,xmm4
    
    !PCLMULQDQ xmm1,xmm0,00h
    !PCLMULQDQ xmm2,xmm0,00h
    !PCLMULQDQ xmm3,xmm0,00h
    !PCLMULQDQ xmm4,xmm0,00h
    
    !PCLMULQDQ xmm5,xmm0,11h
    !PCLMULQDQ xmm6,xmm0,11h
    !PCLMULQDQ xmm7,xmm0,11h
    !PCLMULQDQ xmm8,xmm0,11h
    
    !PXOR xmm1,xmm5
    !PXOR xmm2,xmm6
    !PXOR xmm3,xmm7
    !PXOR xmm4,xmm8
    
    !PXOR xmm1,[r8]
    !PXOR xmm2,[r8+10h]
    !PXOR xmm3,[r8+20h]
    !PXOR xmm4,[r8+30h]
    
    !SUB r9,40h
    !ADD r8,40h
    !CMP r9,40h
    !JGE loop_64
    
    !MOVDQA xmm0,[Lconstant_R4R3]
    !PREFETCHNTA [r8]
    
    !MOVDQA xmm5,xmm1
    !PCLMULQDQ xmm1,xmm0,00h
    !PCLMULQDQ xmm5,xmm0,11h
    !PXOR xmm1,xmm5
    !PXOR xmm1,xmm2
    
    !MOVDQA xmm5,xmm1
    !PCLMULQDQ xmm1,xmm0,00h
    !PCLMULQDQ xmm5,xmm0,11h
    !PXOR xmm1,xmm5
    !PXOR xmm1,xmm3
    
    !MOVDQA xmm5,xmm1
    !PCLMULQDQ xmm1,xmm0,00h
    !PCLMULQDQ xmm5,xmm0,11h
    !PXOR xmm1,xmm5
    !PXOR xmm1,xmm4
    
    !PCLMULQDQ xmm0,xmm1,01h
    !PSRLDQ xmm1,08h
    !PXOR xmm1,xmm0
    
    !MOVDQA xmm2,xmm1
    
    !MOVDQA xmm0,[Lconstant_R5]
    !MOVDQA xmm3,[Lconstant_mask32]
    
    !PSRLDQ xmm2,04h
    !PAND xmm1,xmm3
    !PCLMULQDQ xmm1,xmm0,00h
    !PXOR xmm1,xmm2
    
    !MOVDQA xmm0,[Lconstant_RUpoly]
    
    !MOVDQA xmm2,xmm1
    !PAND xmm1,xmm3
    !PCLMULQDQ xmm1,xmm0,10h
    !PAND xmm1,xmm3
    !PCLMULQDQ xmm1,xmm0,00h
    !PXOR xmm1,xmm2
    !PEXTRD eax,xmm1,01h                 ;eax ist CRC32 (bis hierher)
    !less_64:                              ;Rest (max.63 Bytes)
    
    !MOV r8,[p.v_Mem]
    !MOV r9,[p.v_Laenge]
    !ADD r8,r9
    !AND r9,3fh                          ;möglicher Rest
    !JZ NoRest                            ;kein Rest
    !LEA r10,[CRC_Table]                 ;die restlichen Bytes mit Tabelle
    !SUB r8,r9
    !@@:
    !MOVZX rcx,byte[r8]
    !XOR cl,al
    !SHR rax,8
    !XOR eax,dword[r10+rcx*4]
    !INC r8
    !DEC r9
    !JNZ @b
    !NoRest:
    !NOT eax                             ;wegen Polynomial Reversed
    ProcedureReturn
  CompilerElse
    !MOV eax,[p.v_Laenge]
    !OR eax,eax                          ;erstmal Test, ob Länge = 0
    !JNZ Laenge_OK                        ;nicht 0
    ProcedureReturn                       ;Rückgabewert = 0 für Länge = 0
    
    !Laenge_OK:
    ;Test auf PCLMULQDQ
    !MOV eax,1
    !CPUID
    !MOV eax,[p.v_Crc32]                  ;Initialisierungswert für CRC32
    
    !TEST ecx,2
    !JNZ CL_OK                            ;kann losgehen! Auskommentieren für Test ohne PCLMULQDQ
    
    !LEA ecx,[CRC_Table]                 ;die CPU bringts nicht, also konventionell
    !MOV eax,[p.v_Mem]
    !MOV edx,[p.v_Laenge]
    !PUSH ebx
    !MOV ebx,ecx
    !PUSH esi
    !MOV esi,eax
    !@@:
    !MOVZX ecx,byte[esi]
    !XOR cl,al
    !SHR eax,8
    !XOR eax,dword[ebx+ecx*4]
    !INC esi
    !DEC edx                             ;Länge
    !JNZ @b
    
    !POP esi
    !POP ebx
    !NOT eax                             ;wegen Polynomial Reversed
    ProcedureReturn
    
    !CL_OK:
    !MOV eax,0FFFFFFFFh                  ;Initialisierungswert für CRC32
    !MOV edx,[p.v_Laenge]
    !AND edx,0FFFFFFC0h                  ;Länge < 40h?
    !JZ less_64                           ;ja, konventionell weiter
    
    !MOVD xmm5,eax                       ;eax = 0FFFFFFFFh  Initialisierungswert für CRC32
    !MOV ecx,[p.v_Mem]
    !MOVDQA xmm0,[ecx]
    !MOVDQA xmm1,[ecx+10h]
    !MOVDQA xmm2,[ecx+20h]
    !MOVDQA xmm3,[ecx+30h]
    !PXOR xmm0,xmm5
    
    !SUB edx,40h
    !ADD ecx,40h
    
    !loop_64:
    !PREFETCHNTA [ecx+0c0h]              ;Cache "vorfüllen"
    
    !MOVDQA xmm4,xmm0
    !MOVDQA xmm5,xmm1
    !MOVDQA xmm6,xmm2
    !MOVDQA xmm7,xmm3
    
    !PCLMULQDQ xmm0,[Lconstant_R2R1],00h
    !PCLMULQDQ xmm1,[Lconstant_R2R1],00h
    !PCLMULQDQ xmm2,[Lconstant_R2R1],00h
    !PCLMULQDQ xmm3,[Lconstant_R2R1],00h
    
    !PCLMULQDQ xmm4,[Lconstant_R2R1],11h
    !PCLMULQDQ xmm5,[Lconstant_R2R1],11h
    !PCLMULQDQ xmm6,[Lconstant_R2R1],11h
    !PCLMULQDQ xmm7,[Lconstant_R2R1],11h
    
    !PXOR xmm0,xmm4
    !PXOR xmm1,xmm5
    !PXOR xmm2,xmm6
    !PXOR xmm3,xmm7
    
    !PXOR xmm0,[ecx]
    !PXOR xmm1,[ecx+10h]
    !PXOR xmm2,[ecx+20h]
    !PXOR xmm3,[ecx+30h]
    
    !SUB edx,40h
    !ADD ecx,40h
    !CMP edx,40h
    !JGE loop_64
    
    !MOVDQA xmm6,[Lconstant_R4R3]
    !PREFETCHNTA [ecx]
    
    !MOVDQA xmm4,xmm0
    !PCLMULQDQ xmm0,xmm6,00h
    !PCLMULQDQ xmm4,xmm6,11h
    !PXOR xmm0,xmm4
    !PXOR xmm0,xmm1
    
    !MOVDQA xmm4,xmm0
    !PCLMULQDQ xmm0,xmm6,00h
    !PCLMULQDQ xmm4,xmm6,11h
    !PXOR xmm0,xmm4
    !PXOR xmm0,xmm2
    
    !MOVDQA xmm4,xmm0
    !PCLMULQDQ xmm0,xmm6,00h
    !PCLMULQDQ xmm4,xmm6,11h
    !PXOR xmm0,xmm4
    !PXOR xmm0,xmm3
    
    !PCLMULQDQ xmm6,xmm0,01h
    !PSRLDQ xmm0,08h
    !PXOR xmm0,xmm6
    
    !MOVDQA xmm1,xmm0
    
    !MOVDQA xmm6,[Lconstant_R5]
    !MOVDQA xmm2,[Lconstant_mask32]
    
    !PSRLDQ xmm1,04h
    !PAND xmm0,xmm2
    !PCLMULQDQ xmm0,xmm6,00h
    !PXOR xmm0,xmm1
    
    !MOVDQA xmm6,[Lconstant_RUpoly]
    
    !MOVDQA xmm1,xmm0
    !PAND xmm0,xmm2
    !PCLMULQDQ xmm0,xmm6,10h
    !PAND xmm0,xmm2
    !PCLMULQDQ xmm0,xmm6,00h
    !PXOR xmm0,xmm1
    
    !less_64:                              ;Rest (max.63 Bytes)
    !LEA ecx,[CRC_Table]                 ;die restlichen Bytes mit Tabelle
    !MOV eax,[p.v_Mem]
    !MOV edx,[p.v_Laenge]
    !PUSH ebx
    !MOV ebx,ecx
    !PUSH esi
    !MOV esi,eax
    
    !PEXTRD eax,xmm0,01h                 ;eax ist CRC32 (bis hierher)
    
    !ADD esi,edx
    !AND edx,3fh                         ;möglicher Rest
    !JZ NoRest                            ;kein Rest
    !SUB esi,edx
    !@@:
    !MOVZX ecx,byte[esi]
    !XOR cl,al
    !SHR eax,8
    !XOR eax,dword[ebx+ecx*4]
    !INC esi
    !DEC edx
    !JNZ @b
    
    !NoRest:
    !POP esi
    !POP ebx
    !NOT eax                             ;wegen Polynomial Reversed
    ProcedureReturn
  CompilerEndIf
  
  
  
  ;Konstanten
  !Align 16
  !Lconstant_R2R1:
  !dq 0000000154442BD4h, 00000001C6E41596h
  !Lconstant_R4R3:
  !dq 00000001751997D0h, 00000000CCAA009Eh
  !Lconstant_R5:
  !dq 0000000163CD6124h, 0000000000000000h
  !Lconstant_mask32:
  !dq 00000000FFFFFFFFh, 0000000000000000h
  !Lconstant_RUpoly:
  !dq 00000001DB710641h, 00000001F7011641h
  
  !CRC_Table:
  !dd 0
  !dd 077073096h, 0EE0E612Ch, 0990951BAh, 0076DC419h, 0706AF48Fh
  !dd 0E963A535h, 09E6495A3h, 00EDB8832h, 079DCB8A4h, 0E0D5E91Eh
  !dd 097D2D988h, 009B64C2Bh, 07EB17CBDh, 0E7B82D07h, 090BF1D91h
  !dd 01DB71064h, 06AB020F2h, 0F3B97148h, 084BE41DEh, 01ADAD47Dh
  !dd 06DDDE4EBh, 0F4D4B551h, 083D385C7h, 0136C9856h, 0646BA8C0h
  !dd 0FD62F97Ah, 08A65C9ECh, 014015C4Fh, 063066CD9h, 0FA0F3D63h
  !dd 08D080DF5h, 03B6E20C8h, 04C69105Eh, 0D56041E4h, 0A2677172h
  !dd 03C03E4D1h, 04B04D447h, 0D20D85FDh, 0A50AB56Bh, 035B5A8FAh
  !dd 042B2986Ch, 0DBBBC9D6h, 0ACBCF940h, 032D86CE3h, 045DF5C75h
  !dd 0DCD60DCFh, 0ABD13D59h, 026D930ACh, 051DE003Ah, 0C8D75180h
  !dd 0BFD06116h, 021B4F4B5h, 056B3C423h, 0CFBA9599h, 0B8BDA50Fh
  !dd 02802B89Eh, 05F058808h, 0C60CD9B2h, 0B10BE924h, 02F6F7C87h
  !dd 058684C11h, 0C1611DABh, 0B6662D3Dh, 076DC4190h, 001DB7106h
  !dd 098D220BCh, 0EFD5102Ah, 071B18589h, 006B6B51Fh, 09FBFE4A5h
  !dd 0E8B8D433h, 07807C9A2h, 00F00F934h, 09609A88Eh, 0E10E9818h
  !dd 07F6A0DBBh, 0086D3D2Dh, 091646C97h, 0E6635C01h, 06B6B51F4h
  !dd 01C6C6162h, 0856530D8h, 0F262004Eh, 06C0695EDh, 01B01A57Bh
  !dd 08208F4C1h, 0F50FC457h, 065B0D9C6h, 012B7E950h, 08BBEB8EAh
  !dd 0FCB9887Ch, 062DD1DDFh, 015DA2D49h, 08CD37CF3h, 0FBD44C65h
  !dd 04DB26158h, 03AB551CEh, 0A3BC0074h, 0D4BB30E2h, 04ADFA541h
  !dd 03DD895D7h, 0A4D1C46Dh, 0D3D6F4FBh, 04369E96Ah, 0346ED9FCh
  !dd 0AD678846h, 0DA60B8D0h, 044042D73h, 033031DE5h, 0AA0A4C5Fh
  !dd 0DD0D7CC9h, 05005713Ch, 0270241AAh, 0BE0B1010h, 0C90C2086h
  !dd 05768B525h, 0206F85B3h, 0B966D409h, 0CE61E49Fh, 05EDEF90Eh
  !dd 029D9C998h, 0B0D09822h, 0C7D7A8B4h, 059B33D17h, 02EB40D81h
  !dd 0B7BD5C3Bh, 0C0BA6CADh, 0EDB88320h, 09ABFB3B6h, 003B6E20Ch
  !dd 074B1D29Ah, 0EAD54739h, 09DD277AFh, 004DB2615h, 073DC1683h
  !dd 0E3630B12h, 094643B84h, 00D6D6A3Eh, 07A6A5AA8h, 0E40ECF0Bh
  !dd 09309FF9Dh, 00A00AE27h, 07D079EB1h, 0F00F9344h, 08708A3D2h
  !dd 01E01F268h, 06906C2FEh, 0F762575Dh, 0806567CBh, 0196C3671h
  !dd 06E6B06E7h, 0FED41B76h, 089D32BE0h, 010DA7A5Ah, 067DD4ACCh
  !dd 0F9B9DF6Fh, 08EBEEFF9h, 017B7BE43h, 060B08ED5h, 0D6D6A3E8h
  !dd 0A1D1937Eh, 038D8C2C4h, 04FDFF252h, 0D1BB67F1h, 0A6BC5767h
  !dd 03FB506DDh, 048B2364Bh, 0D80D2BDAh, 0AF0A1B4Ch, 036034AF6h
  !dd 041047A60h, 0DF60EFC3h, 0A867DF55h, 0316E8EEFh, 04669BE79h
  !dd 0CB61B38Ch, 0BC66831Ah, 0256FD2A0h, 05268E236h, 0CC0C7795h
  !dd 0BB0B4703h, 0220216B9h, 05505262Fh, 0C5BA3BBEh, 0B2BD0B28h
  !dd 02BB45A92h, 05CB36A04h, 0C2D7FFA7h, 0B5D0CF31h, 02CD99E8Bh
  !dd 05BDEAE1Dh, 09B64C2B0h, 0EC63F226h, 0756AA39Ch, 0026D930Ah
  !dd 09C0906A9h, 0EB0E363Fh, 072076785h, 005005713h, 095BF4A82h
  !dd 0E2B87A14h, 07BB12BAEh, 00CB61B38h, 092D28E9Bh, 0E5D5BE0Dh
  !dd 07CDCEFB7h, 00BDBDF21h, 086D3D2D4h, 0F1D4E242h, 068DDB3F8h
  !dd 01FDA836Eh, 081BE16CDh, 0F6B9265Bh, 06FB077E1h, 018B74777h
  !dd 088085AE6h, 0FF0F6A70h, 066063BCAh, 011010B5Ch, 08F659EFFh
  !dd 0F862AE69h, 0616BFFD3h, 0166CCF45h, 0A00AE278h, 0D70DD2EEh
  !dd 04E048354h, 03903B3C2h, 0A7672661h, 0D06016F7h, 04969474Dh
  !dd 03E6E77DBh, 0AED16A4Ah, 0D9D65ADCh, 040DF0B66h, 037D83BF0h
  !dd 0A9BCAE53h, 0DEBB9EC5h, 047B2CF7Fh, 030B5FFE9h, 0BDBDF21Ch
  !dd 0CABAC28Ah, 053B39330h, 024B4A3A6h, 0BAD03605h, 0CDD70693h
  !dd 054DE5729h, 023D967BFh, 0B3667A2Eh, 0C4614AB8h, 05D681B02h
  !dd 02A6F2B94h, 0B40BBE37h, 0C30C8EA1h, 05A05DF1Bh, 02D02EF8Dh
EndProcedure
Gruß Rene
Skylake QuadCore i7 6700K @4400MHz, MSI Z170A Gaming M5, 64 GB DDR4 @ 2133MHz, B: Ramdisk 32GB, C: Raid0 SATA SSD 1TB, D: Raid0 M2 SSD 1TB, E: Raid0 HDD 8TB, 28" 4K @ RTX2080, Win10 X64
Antworten