Compte tenu d'un byTeArray Ma méthode actuelle consiste à créer un Y a-t-il une meilleure méthode pour cela? P> uint8_t données [n] code> Qu'est-ce qu'une méthode efficace pour rechercher un octet uint8_t recherche code> à l'intérieur Search code > N'est-ce pas l'octet aligné fort>? c'est-à-dire les trois premiers bits de la recherche code> pourraient être dans data [i] code> et les 5 bits suivants dans data [i + 1] code>. p>
bool get_bit (const uint8_t * src, struct interne_state * état) code> fonction ( struct interne_state code> contient un masque à droite, & Code> Ed avec SRC et retourné, Maintenance Taille_t Src_index uint8_t my_register code> et le comparant avec code> à chaque fois, et en utilisant état-> src_index code> et état-> src_mask code> pour obtenir la position de l'octet correspondant. p>
5 Réponses :
Je ne sais pas si ce serait mieux, mais j'utiliserais une fenêtre coulissante.
uint counter = 0, feeder = 8;
uint window = data[0];
while (search ^ (window & 0xff)){
window >>= 1;
feeder--;
if (feeder < 8){
counter++;
if (counter >= data.length) {
feeder = 0;
break;
}
window |= data[counter] << feeder;
feeder += 8;
}
}
//Returns index of first bit of first sequence occurrence or -1 if sequence is not found
return (feeder > 0) ? (counter+1)*8-feeder : -1;
Je ne pense pas que vous puissiez faire beaucoup mieux que cela dans C:
/*
* Searches for the 8-bit pattern represented by 'needle' in the bit array
* represented by 'haystack'.
*
* Returns the index *in bits* of the first appearance of 'needle', or
* -1 if 'needle' is not found.
*/
int search(uint8_t needle, int num_bytes, uint8_t haystack[num_bytes]) {
if (num_bytes > 0) {
uint16_t window = haystack[0];
if (window == needle) return 0;
for (int i = 1; i < num_bytes; i += 1) {
window = window << 8 + haystack[i];
/* Candidate for unrolling: */
for (int j = 7; j >= 0; j -= 1) {
if ((window >> j) & 0xff == needle) {
return 8 * i - j;
}
}
}
}
return -1;
}
Si vous utilisez un type de données plus large - 64 bits, par exemple - vous pouvez émettre une préfetch qui charge n [i + 8] code> via n [i + 15] code> juste comme Vous commencez à travailler sur n [i] code> via n [i + 7] code>. Au moment où vous avez obtenu les 7 premiers octets et commençait à avoir besoin de bits à partir du prochain ensemble de données, vous espérez-vous être dans un registre, prêt à être utilisé, au lieu de caler la CPU en attente de la charge de la mémoire. Traiter avec des problèmes de Endian serait fastidieux, mais l'OP a demandé un "algorithme efficace", par lequel je prends pour signifier "vite".
Je me demande si cela serait encore plus rapide si vous remplacez la boucle interne avec une recherche de table? Quelque chose comme table [Haystack [I-1]] [Haystack [i]] remplacerait une certaine arithmétique avec un accès à la mémoire. Mon hypothèse serait plus lente pour de petites valeurs de num_bytes, mais plus vite une fois la table dans le cache de données?
@Andrewhenle, il s'agira de toute façon car il est juste une analyse linéaire à travers la mémoire, l'amorçage TLB peut aider à
@Peterderivaz, je ne suis pas suivi. Vous pouvez éventuellement remplacer la boucle interne avec des recherches de table huit i>, mais vous allez toujours avoir besoin d'arithmétique (masquage). Vous pouvez également simplement dérouler la boucle interne (par le commentaire source) si cela s'avère être une victoire et que le compilateur ne le fait pas pour vous. Toute façon, vous avez besoin de huit comparaisons pour chaque octet dans Haystack Code> après le Zeroth.
Si vous recherchez un motif de huit bits dans un grand tableau Vous pouvez implémenter une fenêtre coulissante sur 16 valeurs de bit pour vérifier si le motif recherché fait partie des deux octets formant cette valeur de 16 bits.
être portable. Vous devez vous occuper des questions d'endansion qui se fait par ma mise en œuvre en construisant la valeur de 16 bits pour rechercher manuellement le motif. L'octet élevé est toujours l'octet itéré actuellement et l'octet faible est l'octet suivant. Si vous faites une conversion simple comme une fois Voici ma mise en œuvre, y compris certaines impressions de débogage (la fonction renvoie la position bit ou -1 si le modèle était non trouvé): p> valeur = * (((non signé *) pdata) code> Vous rencontrerez des problèmes sur X86 processeurs ... p> valeur Code>, CMP code> et Masque code> sont la configuration CMP code> et masque code> sont décalés. Si le motif n'a pas été trouvé dans Hi Haute octet, la boucle continue en cochant l'octet suivant comme point d'octet de départ. P>
Si AVX2 est acceptable (avec des versions antérieures, elle n'a donc pas fonctionné si bien, mais vous pouvez toujours faire quelque chose), vous pouvez rechercher dans de nombreux endroits en même temps. Je ne pouvais pas tester cela sur ma machine (seulement compiler) de sorte que ce qui suit est davantage à vous donner une idée de la manière dont il pourrait être approché que le code de copie et de coller, donc je vais essayer de l'expliquer plutôt que simplement du décharge de code. L'idée principale est de lire un Alors maintenant pour certains (non testés et incomplets, voir ci-dessous) Code, P> uint64_t code>, de la décaler à droite par toutes les valeurs qui ont un sens (0 à 7), puis pour chacun de ces 8 nouveaux uint64_t code> , Testez si l'octet est là. Petite complication: pour le uint64_t code> de plus de 0, la position la plus élevée ne doit pas être comptée car elle a été décalée de zéros qui pourrait ne pas être dans les données réelles. Une fois que cela est fait, le prochain uint64_t code> doit être lu à un décalage de 7 à partir de l'actuel, sinon une frontière n'est pas vérifiée à travers. C'est bien cependant, des charges non alignées ne sont plus si graves, surtout si elles ne sont pas larges. P> 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);
}
}
Si vous recherchez une grande quantité de mémoire et que vous pouvez vous permettre une configuration coûteuse, une autre approche consiste à utiliser une table de recherche de 64k. Pour chaque valeur possible de 16 bits possible, la table enregistre un octet contenant le décalage de décalage de bits sur lequel l'octet correspondant se produit (+1, donc 0 peut indiquer aucune correspondance). Vous pouvez l'initialiser comme ceci: Notez que le cas où la valeur est décalée de 8 bits n'est pas inclus (la raison sera évidente dans une minute). P> Ensuite, vous pouvez rechercher votre réseau d'octets comme celui-ci: p> Optimisation supplémentaire: p> Cela dépendra de votre architecture de mémoire si cela est plus rapide qu'une boucle déroulante qui n'utilise pas de table de recherche. P> p>
C'est difficile à faire dans c. Vous ne pouvez pas supposer qu'il y a 8 bits dans un octet. Je serais tenté d'utiliser une solution basée sur l'assemblage.
Peut-être que vous pouvez trouver une inspiration ici . Ce n'est pas exactement la même chose, mais conceptuellement similaire.
Se chevauchent des motifs de bits trouvables? Je suggère de convertir les données
code> etsur la recherche code> aux chaînes (un octet par bit) et en utilisantptr = strtstr (lastptr + 1, recherche) code> ououououPTR = strtstr (lastptr + 8, recherche) code>Si vous êtes prêt à oublier bien défini, portable C, vous pouvez probablement accélérer les choses en manipulant des données en morceaux de 32 ou 64 bits, en fonction de l'architecture de votre machine. Ensuite, vous devriez faire face aux problèmes de Endian, notamment sur des architectures peu-endian telles que x86.
Êtes-vous prêt à accepter SSE Intrinsics? (Si oui, jusqu'à quelle version?)
@HAROLD, comment SSE aidera dans ce cas?