オープンソースライブラリへのSVE 2サポートの追加‐パート2


これはオープンソースライブラリにSVE 2最適化を追加するシリーズの第2部です.パート1を読むことができます.前回図書館を見つけたopus これは現在コンパイラ固有の方法でsimdを使用します.今日、このライブラリでSVE 2の最適化を実装しています.
私の最初のステップは、彼らのためのファイル内のsimdSVE2 counterparts . それから、makefileを修正して、それらの拡張機能を使用して適切にコンパイルするときに検出することができます.マシンがSVE 2をサポートできない場合、そのコードをコンパイルする必要はありません.
パッケージの"neon "を検索することで、以下の結果を得ることができます.
find | grep neon

./celt/arm/pitch_neon_intr.lo
./celt/arm/celt_neon_intr.lo
./celt/arm/celt_neon_intr.c
./celt/arm/pitch_neon_intr.o
./celt/arm/pitch_neon_intr.c
./celt/arm/celt_neon_intr.o
./celt/arm/.libs/pitch_neon_intr.o
./celt/arm/.libs/celt_neon_intr.o
./celt/arm/.deps/pitch_neon_intr.Plo
./celt/arm/.deps/celt_neon_intr.Plo
./silk/fixed/arm/.deps/warped_autocorrelation_FIX_neon_intr.Plo
./silk/fixed/arm/warped_autocorrelation_FIX_neon_intr.c
./silk/arm/biquad_alt_neon_intr.lo
./silk/arm/NSQ_neon.c
./silk/arm/NSQ_del_dec_neon_intr.o
./silk/arm/LPC_inv_pred_gain_neon_intr.c
./silk/arm/NSQ_neon.lo
./silk/arm/NSQ_neon.h
./silk/arm/LPC_inv_pred_gain_neon_intr.o
./silk/arm/.libs/NSQ_del_dec_neon_intr.o
./silk/arm/.libs/LPC_inv_pred_gain_neon_intr.o
./silk/arm/.libs/biquad_alt_neon_intr.o
./silk/arm/.libs/NSQ_neon.o
./silk/arm/LPC_inv_pred_gain_neon_intr.lo
./silk/arm/.deps/NSQ_neon.Plo
./silk/arm/.deps/NSQ_del_dec_neon_intr.Plo
./silk/arm/.deps/LPC_inv_pred_gain_neon_intr.Plo
./silk/arm/.deps/biquad_alt_neon_intr.Plo
./silk/arm/biquad_alt_neon_intr.o
./silk/arm/biquad_alt_neon_intr.c
./silk/arm/NSQ_del_dec_neon_intr.c
./silk/arm/NSQ_del_dec_neon_intr.lo
./silk/arm/NSQ_neon.o
残念ながら、これらのファイルにSVE 2のイントインシクスを追加する時間はありません.ですから、1つのファイルに対して絞り込む必要があります.最後のポストでは、私は特に一つのファイルに言及しました.opus/celt/arm/pitch_neon_intr.c . 私がそこから始めて、私が何をするかを見る.
まずは適切なヘッダを含みます.
#ifdef __ARM_FEATURE_SVE
#include <arm_sve.h>
#endif /* __ARM_FEATURE_SVE */ 
最初のループから始まると、コードは次のようになります.
opus_val32 celt_inner_prod_neon(const opus_val16 *x, const opus_val16 *y, int N)
{

int i;
    opus_val32 xy;
    int16x8_t x_s16x8, y_s16x8;
    int32x4_t xy_s32x4 = vdupq_n_s32(0);
    int64x2_t xy_s64x2;
    int64x1_t xy_s64x1;

    for (i = 0; i < N - 7; i += 8) {
        x_s16x8  = vld1q_s16(&x[i]);
        y_s16x8  = vld1q_s16(&y[i]);
        xy_s32x4 = vmlal_s16(xy_s32x4, vget_low_s16 (x_s16x8), vget_low_s16 (y_s16x8));
        xy_s32x4 = vmlal_s16(xy_s32x4, vget_high_s16(x_s16x8), vget_high_s16(y_s16x8));
    }

for (; i < N; i++) {
        xy = MAC16_16(xy, x[i], y[i]);
    }

年代の本質を見上げることによってinstruction set ARMを提供し、我々はすぐにネオンのintrinsicsを表現し、そのsve 2の対応を決定することができます.
の初期化を開始しますvdupq_n_s32 - これはレジスタの全てのレーンを同じ値に設定します.これはSVE 2バージョンですsvdup_lane .
ループ内の最初の内在.vld1q_s16 , 複数の要素を複数のレジスタに読み込むことができます.この場合、負荷x_s16x8 から&x[i] . それはロードの同じタイプのもう一つに続きますy_s16x8 から&y[i] . これはSVE 2バージョンですsvldnf1sh_32 . 次に、xとyの低い部分と、それから使用する高い部分との間に2つの乗算がありますvmlal_s16 指示.これらのSVEバージョンはsvpmullb and svpmullt 下部と上部半分のそれぞれ.また、コールする必要がありますvget_low_s16 and vget_high_s16 , あるいは、SVE 2対応するものもあります:svunpklo and svunpkhi .
すべての上記の調整をした後に、我々は得るものがここにあります:
#ifdef __ARM_FEATURE_SVE2
pus_val32 celt_inner_prod_neon(const opus_val16 *x, const opus_val16 *y, int N)
{
    int i;
    opus_val32 xy;
    svint16_t x_s16x8, y_s16x8;
    svint32_t xy_s32x4 = svdup_lane(0);
    svint64_t xy_s64x2;
    svint64_t xy_s64x1;

    for (i = 0; i < N - 7; i += 8) {
        x_s16x8  = svldnf1sh_s32(&x[i]);
        y_s16x8  = svldnf1sh_s32(&y[i]);
        xy_s32x4 = svpmullb(xy_s32x4, svunpklo (x_s16x8), svunpklo (y_s16x8));
        xy_s32x4 = svpmullb(xy_s32x4, svunpkhi (x_s16x8), svunpkhi (y_s16x8));
    }

    if (N - i >= 4) {
        const int16x4_t x_s16x4 = vld1_s16(&x[i]);
        const int16x4_t y_s16x4 = vld1_s16(&y[i]);
        xy_s32x4 = vmlal_s16(xy_s32x4, x_s16x4, y_s16x4);
        i += 4;
    }

    xy_s64x2 = vpaddlq_s32(xy_s32x4);
    xy_s64x1 = vadd_s64(vget_low_s64(xy_s64x2), vget_high_s64(xy_s64x2));
    xy      = vget_lane_s32(vreinterpret_s32_s64(xy_s64x1), 0);

    for (; i < N; i++) {
        xy = MAC16_16(xy, x[i], y[i]);
    }
#endif
今私たちがしなければならないすべては、私たちがコンパイルすることができて、それを走らせるかどうか見ることです.
CCASFLAGS = -g -O3 -march=armv8-a+sve2 -fvisibility=hidden -D_FORTIFY_SOURCE=2 -W -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes
CCDEPMODE = depmode=gcc3
CFLAGS = -g -O3 -march=armv8-a+sve2 -fvisibility=hidden -D_FORTIFY_SOURCE=2 -W -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes -fvisibility=hidden -W -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes
私は適切なコンパイルフラグをSVE 2の最適化をオンにし、それを移動した-残念ながら、いくつかのビルドエラーがあるので、次の記事で私は次の手順でこれらを解決し、このパッケージにSVE 2の最適化を構築し続けて行きます対処する必要があります.もっとすぐに!