Schnelles CRC16

OP #3727704
Lesenswert?

Ich habe eine Routine geschrieben, die ein CRC16 korrekt berechnet. 
Diese Routine compiliere ich mit dem Microsoft C++ Compiler von VS2005. 
Programmversion cl.exe: 14.00.50727.762

Die Routine arbeitet zwar, könnte aber durchaus schneller sein.

Der Code sieht so aus:
1
//  CRC16/CCITT
2
//  width: 16
3
//  polynome: 0x1021 (x16 + x12 + x5 + 1)
4
//  reflect: yes (bit transmission order: 0,1,2,3,4,5,6,7)
5
static unsigned short CRCtable[] = {
6
    0, 4489, ... 
7
};
8

9
#pragma optimize("gt", on)
10

11
// \brief calculate the CRC 
12
// \param pImageStart pointer to first byte
13
// \param pImageEnd pointer beyond the last last
14
// \param wOldCRC CRC start value, usually 0xFFFF
15
// \return new CRC value
16
WORD CRC16(const BYTE* pImageStart, const BYTE* pImageEnd, WORD wOldCRC)
17
{
18
  WORD wCRC;
19
  WORD w;
20
  const BYTE* pData;
21

22
  wCRC = wOldCRC;
23
  pData = pImageStart;
24

25
  while (pData < pImageEnd)
26
  {
27
    w = (wCRC ^ *pData++) & 0xFF;
28
    wCRC = wCRC >> 8 ^ CRCtable[w];
29
  }
30
  return wCRC;
31
}

Der Compiler erzeugt daraus diesen Code:
1
CRC16:
2
    ; load parameters
3
    mov     ecx, DWORD PTR _pImageStart$[esp-4]
4
    movzx   eax, WORD PTR _wOldCRC$[esp-4]
5
    push    esi
6
    mov      esi, DWORD PTR _pImageEnd$[esp]
7

8
    ; initial check (pImageStart < pImageEnd)
9
    cmp      ecx, esi
10

11

12
    jae      SHORT $LN1@CRC16
13

14
    ; ----- calculation loop -----
15
    movzx   edx, BYTE PTR [ecx]
16
    xor     dl, al
17

18
    movzx   ax, ah
19
    add      ecx, 1
20
    and     edx, 255    ; 000000ffH
21
    movzx   edx, dx
22
    xor      ax, WORD PTR _CRCtable[edx*2]
23
    
24
    cmp      ecx, esi
25
    movzx   eax, ax
26
    jb      SHORT $LL2@CRC16
27
    ; ----- end of calculation loop -----
28

29
    pop esi
30
    ret
Der spannende Teil dieses Codes ist die caculation loop, da diese für 
jedes Byte ausgeführtwird. In dieser Loop sind 10 Assembleranweisungen. 
Dabei habe ich einen Verdacht, dass diese nicht optimal sind. Hier sind 
die (nach meiner Meinung) kritischen Zeilen:
1
    movzx   edx, BYTE PTR [ecx]
2
    xor     dl, al
3
    ; ...
4
    and     edx, 255    ; 000000ffH
5
    movzx   edx, dx
Jede Anweisung ändert das Register DL oder EDX.

In der ersten wird ein Byte in das 32 Bit Register geladen. Das sollte 
die oberen Bytes auf 0 setzen.

Das XOR ändert nur das untere Byte, die drei oberen bleiben 0.

Das AND sorgt dafür, dass ... die oberen Bytes auf 0 gesetzt werden.

Das MOVZX erweitert die unteren 16 Bit (DX) auf 32 Bit, wobei die oberen 
beiden Bytes auf 0 gesetzt werden.


Kann ich meinem Eindruck trauen, dass die letzten beiden Zeilen 
überflüssig sind? Kann der Compiler so schlecht sein?

Gibt es einen andere Algorithmus, gerne auch in 486 Assembler, der 
schneller ist? Dabei könnte man berücksichtigen, dass mit der Routine 
das CRC16 über einen Bereich von mehreren Megabyte berechnet werden 
soll. Ein Epilog für verbleibende x Bytes wäre kein Problem.
Gast #3729001
Lesenswert?

Die byte-weise Verarbeitung der Daten macht den Prozessor wahrscheinlich 
auch langsam. Ich hab vor paar Jahren mal im optimization guide von 
Intel gelesen und da stand drin, dass bei byte-weisen Zugriffen sehr 
viele wait states anfallen. Ich weiß allerdings nicht wie das bei 
aktuellen Prozessoren ist.
Gast #3729082
Lesenswert?

Du kannst z.B. mal Loop Unrolling machen.

Deine Daten sind ja wahrscheinlich länger.

Also z.B: Input 300 bytes,
in der loop immer z.B. 32 bytes verrechen.
Bleibt 300 % 32 -> 12 restbytes zu verrechnen.

Erspart Vergleiche, kostet Codegröße.
OP #3729291
Lesenswert?

foo schrieb:
>> Dabei könnte man berücksichtigen, dass mit der Routine
>> das CRC16 über einen Bereich von mehreren Megabyte berechnet
>> werden soll.
>
> Deine Daten sind ja wahrscheinlich länger.

Ja wirklich, dass wäre also durch n-faches Hinschreiben des 
Schleifenkörpers zu erledigen, wobei vorher die Anzahl durch n dividiert 
werden muss.

> Erspart Vergleiche, kostet Codegröße.
Codegröße ist in der konkreten Anwenung nicht kritische. Das könnte ich 
mal probieren.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren