ホームページ  >  記事  >  バックエンド開発  >  アセンブリを使用して 8 ビットの位置ポップカウント アルゴリズムを最適化するにはどうすればよいでしょうか。具体的には、内部ループに焦点を当て、プリフェッチやスカラー人口カウントなどの手法を利用することによって実現できますか?

アセンブリを使用して 8 ビットの位置ポップカウント アルゴリズムを最適化するにはどうすればよいでしょうか。具体的には、内部ループに焦点を当て、プリフェッチやスカラー人口カウントなどの手法を利用することによって実現できますか?

Patricia Arquette
Patricia Arquetteオリジナル
2024-10-27 09:02:301000ブラウズ

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?

アセンブリを使用してこの 8 ビット位置ポップカウントを最適化するにはどうすればよいですか?

提供されたコードでは、 関数 __mm_add_epi32_inplace_purego はアセンブリを使用して最適化できます。パフォーマンスを向上させます。特に内側のループは、より高速に実行できるように最適化できます。

位置人口をカウントするために提供されるアルゴリズムは、「位置人口カウント」と呼ばれます。このアルゴリズムは機械学習で使用され、一連のバイト内の設定ビット数をカウントします。指定されたコードでは、_mm_add_epi32_inplace_purego が 2 レベルのループで呼び出され、目的は内部ループを最適化することです。

提供されたコードは、主に と呼ばれる 8 ビット整数の配列で動作します。カウントします。内部ループはバイト スライスを反復し、バイトごとに、ビット パターンの配列 (_expand_byte) から対応するビット位置を counts 配列に追加します。 _expand_byte 配列には、各バイトを個別のビットに拡張するビット パターンが含まれています。

アセンブリを使用して内部ループを最適化するには、パフォーマンスを向上させるために汎用レジスターにカウンターを保持する必要があります。ストリーミング動作を改善するために、かなり前もってメモリをプリフェッチします。単純なシフトと加算の組み合わせ (SHRL/ADCL) を使用して、スカラー人口カウントを実装することもできます。

最適化されたアセンブリ コード の例を以下に示します。このコードは特定のプロセッサ アーキテクチャ用に書かれており、他のシステムで実行するには変更が必要な場合があります。

<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>

要約すると、 最適化にはカウンター用の汎用レジスタの使用が含まれます。事前にメモリをプリフェッチし、SHRL/ADCL を使用してスカラー人口カウントを実装します。このアプローチにより、位置人口カウント アルゴリズムのパフォーマンスが大幅に向上します。

以上がアセンブリを使用して 8 ビットの位置ポップカウント アルゴリズムを最適化するにはどうすればよいでしょうか。具体的には、内部ループに焦点を当て、プリフェッチやスカラー人口カウントなどの手法を利用することによって実現できますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。