Maison  >  Article  >  développement back-end  >  Comment pouvez-vous optimiser un algorithme de popcount positionnel de 8 bits à l'aide de l'assemblage, en particulier en vous concentrant sur la boucle interne et en utilisant des techniques telles que la prélecture et le comptage de population scalaire ?

Comment pouvez-vous optimiser un algorithme de popcount positionnel de 8 bits à l'aide de l'assemblage, en particulier en vous concentrant sur la boucle interne et en utilisant des techniques telles que la prélecture et le comptage de population scalaire ?

Patricia Arquette
Patricia Arquetteoriginal
2024-10-27 09:02:301000parcourir

How can you optimize an 8-bit positional popcount algorithm using assembly, specifically by focusing on the inner loop and utilizing techniques like prefetching and scalar population counting?

Comment optimiser ce popcount positionnel 8 bits à l'aide de l'assembly ?

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn