1

組み込み関数を使用して、NEON のチェックサム計算コード (2 の補数加算) を実装しようとしています。現在のチェックサム計算は ARM で実行されています。

私の実装では、メモリから一度に 128 ビットを NEON レジスタにフェッチし、SIMD (加算) を実行し、結果を 128 ビットの数値から 16 ビットの数値に折り畳みます。

すべてが正常に動作しているように見えますが、私の NEON 実装は ARM バージョンよりも多くの時間を消費しています。

ARM バージョンの所要時間: 0.860000 秒 NEON バージョンの所要時間: 1.260000 秒

ノート:

  1. 「time.h」のユーティリティを使用してプロファイリング
  2. サンプル アプリケーションから 10,000 回呼び出されたチェックサム関数、およびすべての関数の完全な実行後に計算された時間

その他の詳細:

  1. 組み込みコードのコンパイルには GNU ツールチェーン (arm-none-linux-gnueabi-gcc) を使用し、arm ツールチェーンは使用しませんでした。
  2. Linux プラットフォーム。
  3. C 組み込みコード。

質問:

  1. NEON バージョンが ARM バージョンよりも時間がかかるのはなぜですか? (バッチ内の最小サイクルの組み込みが使用されるように注意しましたが)

  2. 私が達成したいことをどのように達成しますか?(NEONで効率)

  3. 誰かが私を指摘したり、ARM-NEON の相互運用を一緒に使用するいくつかのサンプル実装 (理論的な実装の論文や講演ではなく、疑似コード/アルゴリズム/コード) を共有できますか?

どんな助けでも大歓迎です。

これが私のコードです:

uint16_t do_csum(const unsigned char * buff, int len)
{
int odd, count, i;

uint32x4_t result = veorq_u32( result, result), sum = veorq_u32( sum, sum); 
uint16x4_t data, data_hi, data_low, data8;
uint16x8_t dataq;
uint16_t result16, disp[20] = {0,0,0,0,0,0,0,0,0,0};

if (len <= 0)
    goto out;
odd = 1 & (unsigned long) buff;
if (odd) {
    uint8x8_t data1 = veor_u8( data1, data1); 
    data1 = (uint16x4_t)vld1_lane_u8((uint8_t *)buff, data1, 0); //result = *buff << 8;
    data1 = (uint16x4_t)vshl_n_u16( data1, 8);

    len--;
    buff++;
    result = vaddw_u16(result, data1);
}
count = len >> 1;       /* nr of 16-bit words.. */
if (count) {
    if (2 & (unsigned long) buff) {
        uint16x4_t data2 = veor_u16( data2, data2); 
        data2 = (uint16x4_t) vld1_lane_u16((uint16_t *)buff, data2, 0); //result += *(unsigned short *) buff;
        count--;
        len -= 2;
        buff += 2;
        result = vaddw_u16( result, data2);
    }
    count >>= 1;        /* nr of 32-bit words.. */
    if (count) {
        if (4 & (unsigned long) buff) {
            uint32x2_t data4 = (uint16x4_t) vld1_lane_u32((uint32_t *) buff, data4, 0);
            count--;
            len -= 4;
            buff += 4;
            result = vaddw_u16( result, data4);
        }
        count >>= 1;    /* nr of 64-bit words.. */
        if (count) {
            if (8 & (unsigned long) buff) {
                uint64x1_t data8 = vld1_u64((uint64_t *) buff); 
                count--;
                len -= 8;
                buff += 8;
                result = vaddw_u16( result,(uint16x4_t)data8);
            }
            count >>= 1;    /* nr of 128-bit words.. */
            if (count) {
                do {
                    dataq = (uint16x8_t)vld1q_u64((uint64_t *) buff); // VLD1.64 {d0, d1}, [r0]
                    count--;
                    buff += 16;

                    sum = vpaddlq_u16(dataq);   
                    vst1q_u16( disp, dataq); // VST1.16 {d0, d1}, [r0]

                    result = vaddq_u32( sum, result);
                } while (count);
            }
            if (len & 8) {
                uint64x1_t data8 =  vld1_u64((uint64_t *) buff); 
                buff += 8;
                result = vaddw_u16( result, (uint16x4_t)data8);
            }
        }
        if (len & 4) {
            uint32x2_t data4 = veor_u32( data4, data4); 

            data4 = (uint16x4_t)vld1_lane_u32((uint32_t *) buff, data4, 0);//result += *(unsigned int *) buff;
            buff += 4;
            result = vaddw_u16( result,(uint16x4_t) data4);
        }
    }
    if (len & 2) {
        uint16x4_t data2 = veor_u16( data2, data2); 
        data2 = (uint16x4_t) vld1_lane_u16((uint16_t *)buff, data2, 0); //result += *(unsigned short *) buff;
        buff += 2;
        result = vaddw_u16( result, data2);
    }
}
if (len & 1){
    uint8x8_t data1 = veor_u8( data1, data1); 
    data1 = (uint16x4_t) vld1_lane_u8((uint8_t *)buff, data1, 0); //result = *buff << 8;
    result = vaddw_u8( result, data1);
}


result16 = from128to16(result);

if (odd)
    result16 = ((result16 >> 8) & 0xff) | ((result16 & 0xff) << 8);

out:
    return result16;
}
4

1 に答える 1

6

あなたが改善できるいくつかのこと:

  • ストアを取り除くdisp- これは ? に残されたデバッグ コードのように見えます。
  • メインループ内で水平方向の加算を行わないでください - ループ内で部分的な (垂直方向の) 合計を行い、ループの後に最後の水平方向の加算を 1 回行うだけです (これを行う方法の例については、この回答を参照してください - これは SSE 用ですが、原則は同じ)
  • gcc -O3 ...コンパイラの最適化から最大限の利益を得るために使用してください
  • 使用しないでくださいgoto。(パフォーマンスには影響しませんが、悪いです。)
于 2012-08-22T06:16:26.507 に答える