Dette indlæg er alene udtryk for skribentens egen holdning.

Hvor svær kan en checksum være at regne ud ?

24. maj kl. 12:3913

Taget i betragtning hvor gammel en idé en checksum er, elsker jeg at der stadig kan gøres store fremskridt med små modifikationer af simple algoritmer.

Philip Koopman har i mange år forsket i CRC algoritmer og fundet ud af at mange af dem vi har brugt er meget langt fra at være optimale til det vi bruger dem til.

Men han har også kigget på almindelige checksums algoritmer og nogle, f.eks "Fletcher" klarer sig egentlig ret godt, mens andre ikke gør.

Undervejs fandt han en lille bitte forbedring, som gør en stor forskel.

Artiklen fortsætter efter annoncen

Her er en klassisk primtals-modulus checksum:

    sum = 0
    for x in i:
        sum = (sum + x) % 65371
    return sum

Her er den kraftigt forbedrede "koopman16" version:

    sum = 0
    for x in i:
        sum = ((sum << 16) + x) % 65371
    return (sum << 16) % 65371

Og her er hvilken forskel de to 16 bit shifts gør:

Illustration: Philip Koopman.

...ca. 1000 gange mindre risiko for uopdaget fejl, i forhold til Fletcher16, helt op til 4kByte blockstørrelse.

Artiklen fortsætter efter annoncen

Han har også lavet 8 og 32 bit versioner, der sammen med den matematiske forklaring på forbedringen kan læses i hans glimrende artikel.

Tip med hatten herfra!

/phk

 

13 kommentarer.  Hop til debatten
Denne artikel er gratis...

...men det er dyrt at lave god journalistik. Derfor beder vi dig overveje at tegne abonnement på Version2.

Digitaliseringen buldrer derudaf, og it-folkene tegner fremtidens Danmark. Derfor er det vigtigere end nogensinde med et kvalificeret bud på, hvordan it bedst kan være med til at udvikle det danske samfund og erhvervsliv.

Og der har aldrig været mere akut brug for en kritisk vagthund, der råber op, når der tages forkerte it-beslutninger.

Den rolle har Version2 indtaget siden 2006 - og det bliver vi ved med.

Debatten
Log ind eller opret en bruger for at deltage i debatten.
settingsDebatindstillinger
10
30. maj kl. 15:26

Den oprindelige algoritme er håbløs, da den reelt bare lægger værdierne sammen og tager modulo 65371. Dermed er rækkefølgen af værdierne underordnet, så "abcd" giver samme checksum som "badc". Det er en uheldig egenskab for en checksum, og kan løses på mange måder, f.eks. som i Koopmans modifikation ved at lave et bitvist skift mellem hvert trin.

Hvis man skal klandre hans modifikation for noget, så er det, at den bruger et 32-bit mellemresultat og kræver en relativt dyr modulo-operation, hvor den oprindelige (hvis x er et 16-bit tal) kan implementere (sum + x) mod 65371 med en 16 bit unsigned add-with-carry og en subtraktion af 65371, hvis carry-bittet bliver sat eller resultatet er større end 65370. Den sidste del kan man endda nøjes med at gøres lige inden retur.

Så selv om modifikationen ser triviel ud, så har den store konsekvenser for køretiden, specielt på små processorer uden hardware division (eller ved direkte implementering i hardware).

Jeg ville nok selv have brugt følgende variant:

  1. sum = 0
  2. for x in i:
  3. sum = ((sum ROR 1) + x) % 65371
  4. return sum

altså bare lave en 1-bits rotation af 16-bit tallet inden x lægges til. %65371 kan dermed stadig laves som en test og en subtraktion. 1-bit rotates er som regel billige selv på små processorer.

11
30. maj kl. 16:39

Du har ret i, at den gamle udgave var håbløs;)

Koopman's ændring virker fornuftig (so so), fordi de laveste 16 bit flyttes til de øverste 16 bit; derved "huskes" tidligere data. Barrel shifteren klarer SHL/ROL (shift left) i et hug.

Du bruger "rotate right" (ROR;), det er bedre end "shift right", men ,,,

Jeg ville bruge linje 3 som: sum = (sum ROR 4) + x; og helt droppe % (modulo).

12
30. maj kl. 17:07

Jeg ville bruge linje 3 som: sum = (sum ROR 4) + x; og helt droppe % (modulo).

Det er kun marginalt bedre end en oprindelige: Ombytning af klumper på 4 værdier kan ikke observeres i checksummen.

13
30. maj kl. 21:55

Det er kun marginalt bedre end en oprindelige: Ombytning af klumper på 4 værdier kan ikke observeres i checksummen.

Jeg må vist køre nogle simulationer, men jeg er ikke helt tryg ved Koopman's testdata: "0x0001, 0x0002, ..."

Men i det mindste er vi enige om ROR y; SHL 16 fylder de lave to bytes med 0'er og erstatter dem med x.

5
25. maj kl. 10:15

Er der en mindre indrykningsfejl i den klassiske algoritme? return burde være uden for løkken, så vidt jeg kan se.

6
25. maj kl. 10:44

At beholde "return" dér gør køretiden konstant, det er ret svært at slå.

7
25. maj kl. 12:11

Så det er slet ikke skiftet, der giver forbedringen, men at den oprindelige algoritme kun laver operationen én gang :-)

8
25. maj kl. 14:41

Den anden fejl PHK har sneget ind er at ændre konstanten fra 65371 som er et primtal mindre end 2^16 til 65731 der er større end 2^16

9
26. maj kl. 08:55

Beklager, jeg havde ret meget mas med at få layoutet til at se fornuftigt ud.

Jeg prøver at rette.

3
24. maj kl. 19:54

Pud ?

"risiko for uopdaget fejl" ? P sandsynlighed u undetected d defect ?

1
24. maj kl. 14:27

At beregne en checksum kræver først at man ved hvad man beregner checksum på og er enig om hvad det er man beregner checksum på.

Det nytter ikke noget at en interessent beregner checksum på x+y+z , mens en anden kun gør det på x+y.

2
24. maj kl. 14:53

At beregne en checksum kræver først at man ved hvad man beregner checksum på og er enig om hvad det er man beregner checksum på.

Det har været kendt som "The End-to-End Principle in Systems Design" siden 1960 (og genopfundet et antal gange sidenhen.)

Men det har ikke rigtig noget at gøre med emnet, som er forbedring af en specifik familie af checksum algoritmer.