4

巨大な配列を xor しよ​​うとして、uint32NEON コプロセッサを使用することにしました。

c私は2つのバージョンを実装しました:

バージョン 1:

uint32_t xor_array_ver_1(uint32_t *array, int size)
{
    uint32x2_t acc = vmov_n_u32(0);
    uint32_t acc1 = 0;
    for (; size != 0; size -= 2) {
        uint32x2_t vec;
        vec = vld1_u32(array);
        array += 2;
        acc = veor_u32(acc, vec);
    }
    acc1 = vget_lane_u32(acc,0) ^ vget_lane_u32(acc,1);
    return acc1;
}

バージョン 2:

uint32_t xor_array_ver_2(uint32_t *array, int size)
{
    uint32x4_t acc = vmovq_n_u32(0);
    uint32_t acc1 = 0;

    for (; size != 0; size -= 4) {
        uint32x4_t vec;
        vec = vld1q_u32(array);
        array += 4;
        acc = veorq_u32(acc, vec);
    }

    acc1 ^= vgetq_lane_u32(acc,0);
    acc1 ^= vgetq_lane_u32(acc,1);
    acc1 ^= vgetq_lane_u32(acc,2);
    acc1 ^= vgetq_lane_u32(acc,3);

    return acc1;
}

上記の 2 つのバージョンを従来の xor 実装と比較すると、次のようになります。

for (i=0; i<arr_size; i++)
        val ^= my_array[i];

私は2つの問題を観察しました:

  1. バージョン 1 のパフォーマンスは同じです。
  2. バージョン 2 は30% 以上向上しています。

  1. もっと良くなるように書き直すことはできますか?どこで次のようmy_arrayに宣言されています uint32_t my_array[BIG_LENGTH];
  2. 通常の xoring コードのパフォーマンスを向上させる非 NEON の方法はありますか? ループを展開しても改善されません。
4

4 に答える 4

1

私は ARM 向けに書いていませんし、NEON にもまったく詳しくありませんが、ARM NEON がパイプライン化されたアーキテクチャであることに依存している次のような考えがありました。

メモリ帯域幅が飽和していることについて Paul R が正しければ、これによるメリットはほとんどないかもしれませんが、次のようにコードを少し再構成するとどうなるでしょうか.....

uint32_t xor_array_ver_2(uint32_t *array, int size)
{
  // Caveat:  'size' must be a positive multiple of 4, otherwise this
  //          code will loop for a very long time... and almost certainly
  //          segfault (or whatever term your system uses).

  uint32x4_t acc = vmovq_n_u32(0);
  uint32x4_t next_vec = vld1q_u32(array);
  uint32_t acc1 = 0;

  for (size-=4, array+=4; size != 0; size-=4) {
     uint32x4_t vec = next_vec;
     array += 4;
     next_vec = vld1q_u32(array);
     acc = veorq_u32(acc, vec);
  }
  acc = veorq_u32(acc, next_vec);

  acc1 ^= vgetq_lane_u32(acc,0);
  acc1 ^= vgetq_lane_u32(acc,1);
  acc1 ^= vgetq_lane_u32(acc,2);
  acc1 ^= vgetq_lane_u32(acc,3);

  return acc1;
}

....次のループで必要になる前に、次のベクター要素のロードを開始することを目的としています。

あなたが試すかもしれない別のちょっとしたひねりはこれです:

uint32_t xor_array_ver_2(uint32_t *array, int size)
{
  // Caveat:  'size' must be a positive multiple of 4, otherwise this
  //          code will loop for a very long time... and almost certainly
  //          segfault (or whatever term your system uses).

  uint32x4_t acc = vmovq_n_u32(0);
  uint32x4_t next_vec = vld1q_u32(&array[size-4]);
  uint32_t acc1 = 0;

  for (size-=8; size>=0; size-=4) {
     uint32x4_t vec = next_vec;
     next_vec = vld1q_u32(&array[size]);
     acc = veorq_u32(acc, vec);
  }
  acc = veorq_u32(acc, next_vec);

  acc1 ^= vgetq_lane_u32(acc,0);
  acc1 ^= vgetq_lane_u32(acc,1);
  acc1 ^= vgetq_lane_u32(acc,2);
  acc1 ^= vgetq_lane_u32(acc,3);

  return acc1;
}
于 2013-10-03T18:52:10.903 に答える