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.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von NicTheQuick »

:allright:
Könnte Fred gleich so einbauen.
Bild
Benutzeravatar
_JON_
Beiträge: 389
Registriert: 30.03.2010 15:24

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von _JON_ »

Aber was ist mit dem x86 Code?
Wenn Ich dort die 3 Zeilen mit dem Initialisierungswert ändere kommt ein falsches Ergebnis raus,
PureBasic 5.46 LTS (Windows x86/x64) | windows 10 x64 Oktober failure
topsoft
Beiträge: 67
Registriert: 16.09.2004 11:55
Wohnort: nrw

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von topsoft »

Gerade getestet. Bei mir gibt es ausser einem Fehler kein Ergebnis. :shock:

X64 geht, X86 geht nicht

[22:36:23] Executable-Typ: Windows - x86 (32bit, Unicode)
[22:36:23] Executable gestartet.
[22:36:31] [ERROR] crc32.pbi (Zeile: 1)
[22:36:31] [ERROR] Ungültiger Speicherzugriff. (Lesefehler an der Adresse 4294967295)
[22:37:00] Das Programm wurde beendet.

Zeile 1 ist:

Code: Alles auswählen

Procedure.l CRC32_CL(Mem, Laenge, Crc32.l)
Oh Oh.
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 »

Natürlich muss dann auch die 32-Bit-Version umgemodelt werden. Wichtig ist das Alignment von *buffer! Muss mind. 16 sein! Bei 32-Bit muss nachgeholfen werden. Das Ganze sieht dann so aus:

Code: Alles auswählen

  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 = 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
    !NOT eax    
    
    !TEST ecx,2
    !JNZ CL_OK                           ;kann losgehen! Auskommentieren für Test ohne PCLMULQDQ
                     
    !MOV edx,[p.v_Laenge]                ;die CPU bringts nicht, also konventionell
    !MOV ecx,[p.v_Mem]
    !PUSH esi
    !MOV esi,ecx
   
    !PUSH ebx
    !LEA ebx,[CRC_Table]
    
    !@@:
    !MOVZX ecx,byte[esi]                 ;Mem
    !XOR cl,al
    !SHR eax,8
    !XOR eax,dword[ebx+ecx*4]            ;Table
    !INC esi
    !DEC edx                             ;Länge
    !JNZ @b
   
    !POP esi
    !POP ebx
    !NOT eax                             ;wegen Polynomial Reversed
    ProcedureReturn
   
    !CL_OK:
    !MOV edx,[p.v_Laenge]
    !AND edx,0FFFFFFC0h                  ;Länge < 40h?
    !JZ less_64                           ;ja, konventionell weiter

    !MOVD xmm5,eax                       ;eax = 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

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 + 16)    ;muss Alignment 16 sein!
    BufferA = *buffer
    If BufferA & $0F                     
      BufferA = *buffer + 16 - (BufferA & $0F)
    EndIf   
    
    If BufferA
      While Not Eof(hFile)
        Anzahl = ReadData(hFile, BufferA, $FFFF)
        Crc32 = crc32_cl(BufferA, Anzahl, Crc32)
      Wend
      FreeMemory(*buffer)
    EndIf
    CloseFile(hFile)
  EndIf
  ProcedureReturn Crc32
EndProcedure
Gruß
Helle
C h r i s :)
Beiträge: 56
Registriert: 12.11.2015 17:41

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von C h r i s :) »

Ich versteh nur asm ( synonym für ich versteh nur bahnhof :mrgreen: )
Benutzeravatar
_JON_
Beiträge: 389
Registriert: 30.03.2010 15:24

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von _JON_ »

Perfekt, danke Helle :allright:
PureBasic 5.46 LTS (Windows x86/x64) | windows 10 x64 Oktober failure
topsoft
Beiträge: 67
Registriert: 16.09.2004 11:55
Wohnort: nrw

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von topsoft »

Hallo,

Ich habe eine 4.026.400.768 Bytes grosse Datei da kommt bei x86 $FFFFFFFF heraus?
Selbe Datei mit x64 ist OK.
Nehme ich eine andere Datei mit 5.291.225.668 Bytes geht x86 und x64 ohne Probleme.
Bin ich der einzige bei dem das so ist?

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
topsoft
Beiträge: 67
Registriert: 16.09.2004 11:55
Wohnort: nrw

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von topsoft »

Anbei mal der Code mit dem ich Teste. x64 geht immer, x86 nicht immer.

Code: Alles auswählen

EnableExplicit
UseCRC32Fingerprint()
Declare.l CRC32_CL(Mem, Laenge, Crc32)

RandomSeed(ElapsedMilliseconds())
Define *buffer = AllocateMemory($FFFF + 16)    ;muss Alignment 16 sein!
Define *bufferA = *buffer
If *bufferA & $0F                     
  *bufferA = *buffer + 16 - (*bufferA & $0F)
EndIf   
If *bufferA
  Define Loop.i
  For Loop = 1 To 1000
    Define Crc32.i = 0
    Define Anzahl.i = Random($FFFF)
    RandomData(*bufferA, Anzahl)
    Crc32 = CRC32_CL(*bufferA, Anzahl, Crc32)
    Debug UCase(RSet(Fingerprint(*bufferA, Anzahl, #PB_Cipher_CRC32), 8, "0")) + "   " + UCase(RSet(Hex(Crc32), 8, "0"))
  Next Loop
  FreeMemory(*buffer)
EndIf
End

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 = 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
    !NOT eax   
   
    !TEST ecx,2
    !JNZ CL_OK                           ;kann losgehen! Auskommentieren für Test ohne PCLMULQDQ
                     
    !MOV edx,[p.v_Laenge]                ;die CPU bringts nicht, also konventionell
    !MOV ecx,[p.v_Mem]
    !PUSH esi
    !MOV esi,ecx
   
    !PUSH ebx
    !LEA ebx,[CRC_Table]
   
    !@@:
    !MOVZX ecx,byte[esi]                 ;Mem
    !XOR cl,al
    !SHR eax,8
    !XOR eax,dword[ebx+ecx*4]            ;Table
    !INC esi
    !DEC edx                             ;Länge
    !JNZ @b
   
    !POP esi
    !POP ebx
    !NOT eax                             ;wegen Polynomial Reversed
    ProcedureReturn
   
    !CL_OK:
    !MOV edx,[p.v_Laenge]
    !AND edx,0FFFFFFC0h                  ;Länge < 40h?
    !JZ less_64                           ;ja, konventionell weiter

    !MOVD xmm5,eax                       ;eax = 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
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von Helle »

Mit

Code: Alles auswählen

UCase(RSet(Hex(Crc32 & $FFFFFFFF), 8, "0"))
ist man bei Debug 32-Bit immer auf der sicheren Seite :mrgreen: !
Also bitte die Ausgabe ändern!
Gruß
Helle

Ja, ja, 32-Bit...
topsoft
Beiträge: 67
Registriert: 16.09.2004 11:55
Wohnort: nrw

Re: Schnelles CRC32 mit PCLMULQDQ

Beitrag von topsoft »

Thx, da muss man mal drauf kommen.
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