Qual è un'alternativa più veloce a un CRC?


27

Sto eseguendo una trasmissione di dati da un dsPIC a un PC e sto eseguendo un CRC a 8 bit per ogni blocco di 512 byte per assicurarmi che non vi siano errori. Con il mio codice CRC abilitato ottengo circa 33 KB / s, senza di esso ottengo 67 KB / s.

Quali sono alcuni algoritmi alternativi di rilevamento degli errori per verificare che sarebbero più veloci?


5
Come viene implementato lo stesso CRC? Bitwise? Quindi passare a un metodo basato su tabella. Bytewise? Considera lo spazio, la complessità e il compromesso temporale coinvolti nell'aumentare la dimensione della tabella a, diciamo, a 16 bit (che opererebbe su due byte contemporaneamente, ma richiederebbe 64 KB di spazio di archiviazione della tabella).
Aidan Cully,

Ho solo 16 KB su RAM e 128 KB di ROM, quindi una tabella da 64 KB non è un'opzione.
FigBug,

1
Quindi stai usando una tabella a 256 byte? o bit a bit CRC? Se stai facendo bit a bit, byte a byte (con una tabella a 256 byte) sarebbe 8 volte più veloce.
Aidan Cully,

Al momento, proverò un tavolo da 256
FigBug,

1
Da 67kb / sa 33kb / s? Non sono sicuro di ciò che coinvolge le altre tue elaborazioni, ma sembra un po 'eccessivo, anche per un PIC. Forse ci sono altri problemi che impediscono la tua performance?
Rei Miyasaka,

Risposte:


41

Sebbene possano esserci opzioni più rapide di CRC, se le utilizzi è probabile che finirai per sacrificare un certo grado di capacità di rilevamento degli errori. A seconda dei requisiti di rilevamento degli errori, un'alternativa potrebbe essere quella di utilizzare il codice CRC ottimizzato per l'applicazione.

Per un confronto di CRC con altre opzioni, vedere l' eccellente risposta di Martin Thompson .

Un'opzione per aiutare con questo è pycrc che è uno strumento (scritto in Python 1 ) che può generare codice sorgente C per dozzine di combinazioni di modello crc e algoritmo . Ciò consente di ottimizzare la velocità e le dimensioni per la propria applicazione selezionando e confrontando diverse combinazioni. 1: richiede Python 2.6 o successivo.

Supporta il crc-8 modello , ma supporta anche crc-5, crc-16e crc-32tra gli altri. Per quanto riguarda gli algoritmi , supporta bit-by-bit, bit-by-bit-faste table-driven.

Ad esempio (scaricando l'archivio):

$ wget --quiet http://sourceforge.net/projects/pycrc/files/pycrc/pycrc-0.8/pycrc-0.8.tar.gz/download
$ tar -xf pycrc-0.8.tar.gz
$ cd pycrc-0.8
$ ./pycrc.py --model=crc-8 --algorithm=bit-by-bit      --generate c -o crc8-byb.c
$ ./pycrc.py --model=crc-8 --algorithm=bit-by-bit-fast --generate c -o crc8-bybf.c
$ ./pycrc.py --model=crc-8 --algorithm=table-driven    --generate c -o crc8-table.c
$ ./pycrc.py --model=crc-16 --algorithm=table-driven   --generate c -o crc16-table.c
$ wc *.c
   72   256  1790 crc8-byb.c
   54   190  1392 crc8-bybf.c
   66   433  2966 crc8-table.c
  101   515  4094 crc16-table.c
  293  1394 10242 total

Puoi anche fare cose funky come specificare utilizzando ricerche a doppio nibble (con una tabella di ricerca a 16 byte) anziché una ricerca a byte singolo, con una tabella di ricerca a 256 byte.

Ad esempio (clonazione del repository git):

$ git clone http://github.com/tpircher/pycrc.git
$ cd pycrc
$ git branch
* master
$ git describe
v0.8-3-g7a041cd
$ ./pycrc.py --model=crc-8 --algorithm=table-driven --table-idx-width=4 --generate c -o crc8-table4.c
$ wc crc8-table4.c
  53  211 1562 crc8-table4.c

Dati i limiti di memoria e velocità, questa opzione potrebbe essere il miglior compromesso tra velocità e dimensioni del codice. L'unico modo per essere sicuri sarebbe di confrontarlo però.


Il repository pycrc git è su github , così come il tracker dei problemi , ma può anche essere scaricato da sourceforge .


Non credo che la maggior parte delle persone che scrivono cose per il PIC stiano usando C, ma in questo caso potrebbe funzionare.
Billy ONeal,

4
@Billy - Davvero? Non credo di aver trovato qualcuno in via di sviluppo commerciale per PIC che non usasse C. Non ho certamente la pazienza per l'assemblatore in questi giorni e C ben strutturato può finire per essere piuttosto compatto.
Mark Booth,

Sto usando un dsPIC e sto usando C.
FigBug

@FigBug - Grazie, felice che ti piaccia la mia risposta. Se esegui alcuni test di benchmark, non esitare a modificare la mia risposta con i tuoi risultati. Mi piacerebbe sapere quanta differenza fa ciascuno degli algoritmi in termini di throughput dell'applicazione e footprint di memoria.
Mark Booth,

1
Un altro voto per pyCrc qui. usandolo in vari progetti con vincoli diversi ed è semplicemente fantastico.
Vicky,

11

La semplice parità a un bit (fondamentalmente XORing dei dati su se stessi ripetutamente) è più veloce di quanto si possa ottenere. Però perdi molto del controllo degli errori di un CRC.

In pseudocodice:

char checksum = 0;
for each (char c in buffer)
{
    checksum ^= c;
    SendToPC(c);
}
SendToPc(checksum);

1
Ci ho pensato un po 'di tempo fa. Io credo che sommando invece di XOR effettivamente funziona un po 'meglio. (normalmente sommare tutto, quindi trasmettere il complemento 2 della somma come somma di controllo. Sul ricevitore, sommare tutto compreso quella somma di controllo ricevuta. Il risultato è 0 se tutto va bene e non-0 altrimenti.)
quick_now

1
@quickly: Non penso che ci sia una differenza significativa tra quei due - nessuno dei due metodi fornisce una garanzia così buona che le cose non siano state corrotte. Se aggiungi è più veloce sull'architettura di destinazione, utilizzalo invece.
Billy ONeal,

7
Ho ricordato: la principale differenza tra ADD e XOR è che c'è meno rilevabilità di errori a più bit. Nel caso di un flusso di byte, gli errori nella stessa posizione di bit vengono annullati utilizzando XOR. Quando si utilizza ADD, la propagazione di bit verso l'alto attraverso un byte di checksum significa che questo caso è più rilevabile. (Tuttavia, è probabile che errori a bit multipli in bit diversi distribuiti nel flusso di byte siano meno rilevabili, a seconda delle circostanze al momento). Qualsiasi accordo di checksum come questo è TERRIBILE per errori a più bit, quindi è un argomento abbastanza secondario.
quick_now

XOR è molto meno utile di CRC.

3
@ Thorbjørn: credo di averlo riconosciuto nella mia risposta. :)
Billy ONeal,

10

Un documento davvero valido che confronta le prestazioni di vari checksum e CRC in un contesto incorporato:

L'efficacia dei checksum per le reti integrate

Alcune citazioni dalle conclusioni (basate sui loro studi sulle probabilità di errore non rilevate):

Quando dominano gli errori di scoppio

XOR, l'aggiunta del complemento a due e i checksum CRC forniscono prestazioni di rilevamento degli errori migliori rispetto all'aggiunta del complemento, i checksum Fletcher e Adler.

In altre applicazioni

un "buon" polinomio CRC, ove possibile, dovrebbe essere utilizzato per scopi di rilevamento degli errori

Se il costo di calcolo è molto limitato

(come nel tuo caso), utilizzare (in ordine di efficacia):

Altre citazioni:

Il checksum Fletcher ha un costo computazionale inferiore rispetto al checksum Adler e, contrariamente a quanto si pensa, è anche più efficace nella maggior parte delle situazioni.

e

In genere non vi è motivo di continuare la pratica comune di utilizzare un checksum XOR in nuovi progetti, poiché ha gli stessi costi di calcolo del software di un checksum basato su addizioni ma è solo circa la metà efficace nel rilevare errori.


1
Come bonus, un checksum Fletcher è molto facile da implementare.
RubberDuck,

6

Il checksum di Adler dovrebbe essere sufficiente per verificare le distorsioni della trasmissione. È utilizzato dalla libreria di compressione Zlib ed è stato adottato da Java 3D Mobile Graphics Standard per fornire un controllo rapido ma efficace dell'integrità dei dati.

Dalla pagina di Wikipedia :

Un checksum Adler-32 si ottiene calcolando due checksum a 16 bit A e B e concatenando i loro bit in un numero intero a 32 bit. A è la somma di tutti i byte nella stringa più uno e B è la somma dei singoli valori di A per ogni passaggio.

All'inizio di una corsa di Adler-32, A viene inizializzato su 1, B su 0. Le somme sono fatte modulo 65521 (il numero primo più grande minore di 2 ^ 16 o 65536). I byte sono memorizzati in ordine di rete (big endian), B occupa i due byte più significativi.

La funzione può essere espressa come

 A = 1 + D1 + D2 + ... + Dn (mod 65521)
 B = (1 + D1) + (1 + D1 + D2) + ... + (1 + D1 + D2 + ... + Dn) (mod 65521)
   = n×D1 + (n-1)×D2 + (n-2)×D3 + ... + Dn + n (mod 65521)

 Adler-32(D) = B × 65536 + A

dove D è la stringa di byte per cui calcolare la somma di controllo e n è la lunghezza di D.


Si noti che Adler32 è quasi inutile per brevi tirature di dati. Fino a circa 180 byte, produce numerose collisioni.
Greyfade,

+1: una via di mezzo ragionevole tra un CRC e una parità di bit semplice.
Billy ONeal,

@greyfade - FigBug menzionato usando blocchi da 512 byte, quindi questo non dovrebbe essere un problema per l'OP. È bene averlo notato per le persone con altri requisiti.
Mark Booth,

5

Non sono a conoscenza di qualcosa di efficace nel rilevamento degli errori quanto un CRC e più veloce - se ci fosse, le persone lo userebbero invece.

Potresti provare un semplice checksum, ma è molto meno probabile che rilevi errori.


2
Sono disposto a rinunciare a un'efficacia per la velocità.
FigBug,

3

Bene, la stessa logica di checksum è buona e le persone possono aiutare con algoritmi più veloci.

Se si desidera migliorare la velocità del componente, potrebbe essere necessario esaminare la modifica della tecnica complessiva per separare il componente di trasferimento dal componente di convalida.

Se hai questi come due elementi indipendenti (su thread diversi), puoi ottenere la massima velocità di trasferimento e inviare nuovamente solo i pacchetti non riusciti.

L'algoritmo sarebbe simile a:

  • Il server si suddivide in dimensioni di pacchetti note (ad esempio, blocchi da 1K). Li mette in una coda di "da inviare".
  • Ogni pacchetto viene inviato con un ID a 16 o 32 bit E il suo checksum.
  • Il client riceve ogni pacchetto e lo inserisce in una coda per l'elaborazione.
  • Su un thread separato il client accetta un pacchetto alla volta, esegue la convalida.
    • In caso di successo, lo aggiunge alla raccolta finale di pacchetti (in ordine ID)
    • In caso di errore riporta l'ID non riuscito al server, che mette in coda quel pacchetto per essere reinviato.
  • Una volta che hai ricevuto e convalidato i pacchetti e hai gli ID nel sequnce corretto (a partire da 1) puoi iniziare a scriverli su disco (o fare tutto ciò che è richiesto).

Ciò ti consentirà di trasmettere alla massima velocità possibile e, se giochi con le dimensioni del tuo pacchetto, puoi calcolare la percentuale di fallimento ottimale VS la frequenza di convalida / rinvio.


2

I checksum sono tradizionali

(riduci # '+ stream)

Anche XOR come indicato sopra funzionerebbe

(ridurre il flusso # 'XOR)

Uno schema leggermente più elaborato (più lento) è il controllo di parità standard per le connessioni seriali.

A questo livello, stai scambiando la correttezza per la velocità. Questi occasionalmente falliranno.

Al livello più sofisticato successivo, puoi usare alcune cose di tipo crc / hash.

Un altro progetto sarebbe quello di aumentare le dimensioni del blocco utilizzato per il flusso.

Dovresti avere una stima del tasso di errore effettivo per ottimizzare la selezione dell'algoritmo e i parametri per la dimensione del blocco.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.