2016-05-22 22 views
8

Recentemente ho giocato con CloudFlare's optimized zlib e i risultati sono davvero impressionanti.Come utilizzare correttamente l'assembly di moltiplicazione senza porta (PCLMULQDQ) in zlib CRC32?

Sfortunatamente, sembra che lo sviluppo di Zlib sia stato abbandonato e la loro forcella si è rotta. Alla fine sono stato in grado di manually rebase their changes sul ramo current zlib development, anche se era un vero rompicoglioni.

In ogni caso, c'è ancora una importante ottimizzazione nel codice CloudFlare non sono stato in grado di utilizzare, cioè, le fast CRC32 code implemented with the PCLMULQDQ istruzioni di moltiplicazione di riporto meno inclusi con le nuove (Haswell e più tardi, credo) processori Intel, a causa :

  1. sono su un Mac, e né il clangore integrato assemblatore né antica GAS di Apple capisco le mnemonica GAS nuova generazione usati, e

  2. il codice è stato revocato dal kernel di Linux ed è GPL2, che rende l'intera libreria GPL2, e quindi fondamentalmente lo rende inutile per i miei scopi.

così ho fatto un po 'di caccia in giro, e dopo qualche ora mi sono imbattuto su un codice che Apple sta usando nella loro bzip2: scritti a mano, le implementazioni CRC32 vectorized sia per arm64 e x86_64.

Stranamente, i commenti per l'assemblaggio x86_64 sono (solo) nella sorgente arm64, ma sembra indicare che questo codice potrebbe essere utilizzato con zlib:

This function SHOULD NOT be called directly. It should be called in a wrapper 
function (such as crc32_little in crc32.c) that 1st align an input buffer to 16-byte (update crc along the way), 
and make sure that len is at least 16 and SHOULD be a multiple of 16. 

Ma io, purtroppo, dopo alcuni tentativi, a questo punto mi sembra di essere un po 'sopra la mia testa. E non sono sicuro di come lo esegua effettivamente quello. Quindi speravo che qualcuno potesse mostrarmi come/dove si chiamerebbe la funzione fornita.

(Sarebbe anche fantastico se ci fosse un modo per farlo dove sono state rilevate le funzionalità necessarie in fase di esecuzione, e potrebbe ricadere sull'implementazione del software se le funzionalità hardware non sono disponibili, quindi non avrei dovuto distribuire binari multipli. Ma, per lo meno, se qualcuno mi può aiutare a capire come ottenere correttamente la libreria CRC32 basata su Apple PCLMULQDQ, sarebbe una lunga strada, a prescindere.)

+0

è possibile enumerare caratteristiche hardware in fase di esecuzione con l'istruzione CPUID. Controlla la documentazione di Intel. – James

risposta

4

Come dice, è necessario calcolare la somma CRC su un buffer allineato a 16 byte che ha una lunghezza di più di 16 byte. Pertanto, il puntatore del buffer corrente viene impostato come uintptr_t e fino a quando i suoi 4 bit LSB non sono zero, si aumenta il puntatore che alimenta i byte in una normale routine CRC-32. Una volta raggiunto l'indirizzo allineato a 16 byte, arrotondare la lunghezza rimanente a più di 16, quindi inviare questi byte al CRC-32 veloce e di nuovo i byte rimanenti al calcolo lento.


Qualcosa di simile:

// a function for adding a single byte to crc 
uint32_t crc32_by_byte(uint32_t crc, uint8_t byte); 

// the assembly routine 
uint32_t _crc32_vec(uint32_t crc, uint8_t *input, int length); 

uint32_t crc = initial_value; 
uint8_t *input = whatever; 
int length = whatever; // yes, the assembly uses *int* length. 

assert(length >= 32); // if length is less than 32 just calculate byte by byte 
while ((uintptr_t)input & 0xf) { // for as long as input is not 16-byte aligned 
    crc = crc32_by_byte(crc, *input++); 
    length--; 
} 

// input is now 16-byte-aligned 
// floor length to multiple of 16 
int fast_length = (length >> 4) << 4; 
crc = _crc32_vec(crc, input, fast_length); 

// do the remaining bytes 
length -= fast_length; 
while (length--) { 
    crc = crc32_by_byte(crc, *input++) 
} 
return crc;