Dans le code fourni, la fonction __mm_add_epi32_inplace_purego peut être optimisée à l'aide de l'assembly pour améliorer ses performances. La boucle interne en particulier peut être optimisée pour une exécution plus rapide.
L'algorithme fourni pour compter la population positionnelle est appelé « décompte de population positionnelle ». Cet algorithme est utilisé dans l'apprentissage automatique et consiste à compter le nombre de bits définis dans une série d'octets. Dans le code donné, _mm_add_epi32_inplace_purego est appelé dans deux niveaux de boucle, et le but est d'optimiser la boucle interne.
Le code fourni fonctionne principalement sur un tableau d'entiers de 8 bits appelé compte. La boucle interne parcourt une tranche d'octets et, pour chaque octet, ajoute les positions de bits correspondantes d'un tableau de modèles de bits (_expand_byte) au tableau de comptes. Le tableau _expand_byte contient des modèles de bits qui développent chaque octet en ses bits individuels.
Pour optimiser la boucle interne à l'aide de l'assembly, vous devez conserver les compteurs dans des registres à usage général pour de meilleures performances et pré-extraire la mémoire bien à l’avance pour améliorer le comportement de streaming. Vous pouvez également implémenter un comptage de population scalaire à l'aide d'une simple combinaison de décalage et d'ajout (SHRL/ADCL).
Un exemple de code assembleur optimisé est fourni ci-dessous. Ce code est écrit pour une architecture de processeur spécifique et devra peut-être être modifié pour fonctionner sur d'autres systèmes.
<code class="assembly">#include "textflag.h" // func PospopcntReg(counts *[8]int32, buf []byte) TEXT ·PospopcntReg(SB),NOSPLIT,-32 MOVQ counts+0(FP), DI MOVQ buf_base+8(FP), SI // SI = &buf[0] MOVQ buf_len+16(FP), CX // CX = len(buf) // load counts into register R8--R15 MOVL 4*0(DI), R8 MOVL 4*1(DI), R9 MOVL 4*2(DI), R10 MOVL 4*3(DI), R11 MOVL 4*4(DI), R12 MOVL 4*5(DI), R13 MOVL 4*6(DI), R14 MOVL 4*7(DI), R15 SUBQ , CX // pre-subtract 32 bit from CX JL scalar vector: VMOVDQU (SI), Y0 // load 32 bytes from buf PREFETCHT0 384(SI) // prefetch some data ADDQ , SI // advance SI past them VPMOVMSKB Y0, AX // move MSB of Y0 bytes to AX POPCNTL AX, AX // count population of AX ADDL AX, R15 // add to counter VPADDD Y0, Y0, Y0 // shift Y0 left by one place VPMOVMSKB Y0, AX // move MSB of Y0 bytes to AX POPCNTL AX, AX // count population of AX ADDL AX, R14 // add to counter VPADDD Y0, Y0, Y0 // shift Y0 left by one place VPMOVMSKB Y0, AX // move MSB of Y0 bytes to AX POPCNTL AX, AX // count population of AX ADDL AX, R13 // add to counter VPADDD Y0, Y0, Y0 // shift Y0 left by one place VPMOVMSKB Y0, AX // move MSB of Y0 bytes to AX POPCNTL AX, AX // count population of AX ADDL AX, R12 // add to counter VPADDD Y0, Y0, Y0 // shift Y0 left by one place VPMOVMSKB Y0, AX // move MSB of Y0 bytes to AX POPCNTL AX, AX // count population of AX ADDL AX, R11 // add to counter VPADDD Y0, Y0, Y0 // shift Y0 left by one place VPMOVMSKB Y0, AX // move MSB of Y0 bytes to AX POPCNTL AX, AX // count population of AX ADDL AX, R10 // add to counter VPADDD Y0, Y0, Y0 // shift Y0 left by one place VPMOVMSKB Y0, AX // move MSB of Y0 bytes to AX POPCNTL AX, AX // count population of AX ADDL AX, R9 // add to counter VPADDD Y0, Y0, Y0 // shift Y0 left by one place VPMOVMSKB Y0, AX // move MSB of Y0 bytes to AX POPCNTL AX, AX // count population of AX ADDL AX, R8 // add to counter SUBQ , CX JGE vector // repeat as long as bytes are left scalar: ADDQ , CX // undo last subtraction JE done // if CX=0, there's nothing left loop: MOVBLZX (SI), AX // load a byte from buf INCQ SI // advance past it SHRL , AX // CF=LSB, shift byte to the right ADCL , R8 // add CF to R8 SHRL , AX ADCL , R9 // add CF to R9 SHRL , AX ADCL , R10 // add CF to R10 SHRL , AX ADCL , R11 // add CF to R11 SHRL , AX ADCL , R12 // add CF to R12 SHRL , AX ADCL , R13 // add CF to R13 SHRL , AX ADCL , R14 // add CF to R14 SHRL , AX ADCL , R15 // add CF to R15 DECQ CX // mark this byte as done JNE loop // and proceed if any bytes are left // write R8--R15 back to counts done: MOVL R8, 4*0(DI) MOVL R9, 4*1(DI) MOVL R10, 4*2(DI) MOVL R11, 4*3(DI) MOVL R12, 4*4(DI) MOVL R13, 4*5(DI) MOVL R14, 4*6(DI) MOVL R15, 4*7(DI) VZEROUPPER // restore SSE-compatibility RET</code>
En résumé, l'optimisation implique l'utilisation de registres à usage général pour les compteurs, précharger la mémoire à l'avance et implémenter un comptage de population scalaire à l'aide de SHRL/ADCL. Cette approche peut améliorer considérablement les performances de l'algorithme de décompte de population positionnel.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!