Se AVX2 è accettabile (con le versioni precedenti non ha funzionato fuori così bene, ma puoi ancora fare qualcosa lì), puoi cercare in molti posti allo stesso tempo.Non ho potuto testarlo sulla mia macchina (solo compilare) quindi il seguente è più per darti un'idea di come potrebbe essere affrontato che copiare il codice di scrittura &, quindi cercherò di spiegarlo piuttosto che solo il dump del codice .
L'idea principale è quella di leggere uno uint64_t
, spostarlo verso destra da tutti i valori che hanno senso (da 0 a 7), quindi per ciascuno di questi 8 nuovi uint64_t
, verificare se il byte è lì dentro. Piccola complicazione: per lo uint64_t
spostato di più di 0, la posizione più alta non dovrebbe essere conteggiata poiché ha degli zeri spostati in esso che potrebbero non essere nei dati effettivi. Una volta fatto, il prossimo uint64_t
dovrebbe essere letto con un offset di 7 rispetto a quello corrente, altrimenti c'è un confine che non viene controllato attraverso. Va bene però, i carichi non allineati non sono più così male, soprattutto se non sono larghi.
Così ora per un po 'di codice (non testato, e incompleta, vedi sotto),
__m256i needle = _mm256_set1_epi8(find);
size_t i;
for (i = 0; i < n - 6; i += 7) {
// unaligned load here, but that's OK
uint64_t d = *(uint64_t*)(data + i);
__m256i x = _mm256_set1_epi64x(d);
__m256i low = _mm256_srlv_epi64(x, _mm256_set_epi64x(3, 2, 1, 0));
__m256i high = _mm256_srlv_epi64(x, _mm256_set_epi64x(7, 6, 5, 4));
low = _mm256_cmpeq_epi8(low, needle);
high = _mm256_cmpeq_epi8(high, needle);
// in the qword right-shifted by 0, all positions are valid
// otherwise, the top position corresponds to an incomplete byte
uint32_t lowmask = 0x7f7f7fffu & _mm256_movemask_epi8(low);
uint32_t highmask = 0x7f7f7f7fu & _mm256_movemask_epi8(high);
uint64_t mask = lowmask | ((uint64_t)highmask << 32);
if (mask) {
int bitindex = __builtin_ffsl(mask);
// the bit-index and byte-index are swapped
return 8 * (i + (bitindex & 7)) + (bitindex >> 3);
}
}
Il divertente "bit-index e byte-index sono scambiati" cosa è perché la ricerca all'interno di un QWORD è fatto di byte da byte e i risultati di tali confronti finiscono in 8 bit adiacenti, mentre la ricerca di "spostati di 1" finisce negli 8 bit successivi e così via. Quindi nelle maschere risultanti, l'indice del byte che contiene l'1 è un offset di bit, ma l'indice di bit all'interno di quel byte è in realtà l'offset di byte, ad esempio 0x8000 corrisponderebbe alla ricerca del byte al settimo byte di il qword che è stato spostato a destra di 1, quindi l'indice attuale è 8 * 7 + 1.
C'è anche il problema della "coda", la parte dei dati rimasti quando tutti i blocchi di 7 byte sono stati elaborati. Può essere fatto allo stesso modo, ma ora più posizioni contengono byte fasulli. Ora i byte n - i
sono rimasti, quindi la maschera deve avere i bit n - i
impostati nel byte più basso e uno in meno per tutti gli altri byte (per lo stesso motivo di prima, le altre posizioni hanno gli zero spostati in). Inoltre, se c'è esattamente 1 byte "a sinistra", non è veramente lasciato perché sarebbe già stato testato, ma non importa. Immagino che i dati siano sufficientemente imbottiti che l'accesso fuori limite non ha importanza. Qui si tratta, non testato:
if (i < n - 1) {
// make n-i-1 bits, then copy them to every byte
uint32_t validh = ((1u << (n - i - 1)) - 1) * 0x01010101;
// the lowest position has an extra valid bit, set lowest zero
uint32_t validl = (validh + 1) | validh;
uint64_t d = *(uint64_t*)(data + i);
__m256i x = _mm256_set1_epi64x(d);
__m256i low = _mm256_srlv_epi64(x, _mm256_set_epi64x(3, 2, 1, 0));
__m256i high = _mm256_srlv_epi64(x, _mm256_set_epi64x(7, 6, 5, 4));
low = _mm256_cmpeq_epi8(low, needle);
high = _mm256_cmpeq_epi8(high, needle);
uint32_t lowmask = validl & _mm256_movemask_epi8(low);
uint32_t highmask = validh & _mm256_movemask_epi8(high);
uint64_t mask = lowmask | ((uint64_t)highmask << 32);
if (mask) {
int bitindex = __builtin_ffsl(mask);
return 8 * (i + (bitindex & 7)) + (bitindex >> 3);
}
}
Questo è difficile da fare in ben definito c. Non puoi supporre che ci siano 8 bit in un byte. Sarei tentato di utilizzare una soluzione basata su assembly. – Bathsheba
Forse puoi trovare qualche ispirazione [qui] (http://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm#Shifting_substrings_search_and_competing_algorithms). Non è esattamente la stessa cosa, ma concettualmente simile. – mkrieger1
Sono disponibili schemi di bit sovrapposti? Suggerisco di convertire 'data' e' search' in stringhe (un byte per bit) e usando 'ptr = strstr (lastptr + 1, search)' o 'ptr = strstr (lastptr + 8, search)' –