6

浮動小数点変数の大きな (~100 000) 配列があり、しきい値 (浮動小数点も) があります。

問題は、配列の各変数をしきい値と比較する必要があることですが、NEON フラグの転送には非常に長い時間がかかります (プロファイラーによると約 20 サイクル)。

これらの値を比較する効率的な方法はありますか?

注:丸め誤差は問題ではないため、次のことを試しました:

float arr[10000];
float threshold; 
....

int a = arr[20]; // e.g.
int t = threshold;
if (t > a) {....}

しかし、この場合、次のプロセッサ コマンド シーケンスを取得します。

vldr.32        s0, [r0]
vcvt.s32.f32   s0, s0
vmov           r0, s0    <--- takes 20 cycles as `vmrs APSR_nzcv, fpscr` in case of 
cmp            r0, r1         floating point comparison

変換は NEON で行われるため、説明した方法で整数を比較するか、浮動小数点数を比較するかは関係ありません。

4

4 に答える 4

5

float が 32 ビットの IEEE-754 で int も 32 ビットであり、+infinity、-infinity、およびNaN値がない場合、ちょっとしたトリックで float を int として比較できます。

#include <stdio.h>
#include <limits.h>
#include <assert.h>

#define C_ASSERT(expr) extern char CAssertExtern[(expr)?1:-1]
C_ASSERT(sizeof(int) == sizeof(float));
C_ASSERT(sizeof(int) * CHAR_BIT == 32);

int isGreater(float* f1, float* f2)
{
  int i1, i2, t1, t2;

  i1 = *(int*)f1;
  i2 = *(int*)f2;

  t1 = i1 >> 31;
  i1 = (i1 ^ t1) + (t1 & 0x80000001);

  t2 = i2 >> 31;
  i2 = (i2 ^ t2) + (t2 & 0x80000001);

  return i1 > i2;
}

int main(void)
{
  float arr[9] = { -3, -2, -1.5, -1, 0, 1, 1.5, 2, 3 };
  float thr;
  int i;

  // Make sure floats are 32-bit IEE754 and
  // reinterpreted as integers as we want/expect
  {
    static const float testf = 8873283.0f;
    unsigned testi = *(unsigned*)&testf;
    assert(testi == 0x4B076543);
  }

  thr = -1.5;
  for (i = 0; i < 9; i++)
  {
    printf("%f %s %f\n", arr[i], "<=\0> " + 3*isGreater(&arr[i], &thr), thr);
  }

  thr = 1.5;
  for (i = 0; i < 9; i++)
  {
    printf("%f %s %f\n", arr[i], "<=\0> " + 3*isGreater(&arr[i], &thr), thr);
  }

  return 0;
}

出力:

-3.000000 <= -1.500000
-2.000000 <= -1.500000
-1.500000 <= -1.500000
-1.000000 >  -1.500000
0.000000 >  -1.500000
1.000000 >  -1.500000
1.500000 >  -1.500000
2.000000 >  -1.500000
3.000000 >  -1.500000
-3.000000 <= 1.500000
-2.000000 <= 1.500000
-1.500000 <= 1.500000
-1.000000 <= 1.500000
0.000000 <= 1.500000
1.000000 <= 1.500000
1.500000 <= 1.500000
2.000000 >  1.500000
3.000000 >  1.500000

もちろん、isGreater()しきい値が変わらない場合は、比較演算子で使用される最終的な整数値を事前に計算することは理にかなっています。

上記のコードで C/C++ の未定義の動作が心配な場合は、アセンブリでコードを書き直すことができます。

于 2012-04-30T11:24:52.617 に答える
2

データがfloatの場合は、floatとの比較を行う必要があります。

float arr[10000];
float threshold;
....

float a = arr[20]; // e.g.
if (threshold > a) {....}

そうしないと、高価なfloat-int変換が発生します。

于 2012-04-30T10:31:23.377 に答える
2

あなたの例は、コンパイラによって生成されたコードがいかに悪いかを示しています:

値を int に変換するためだけに NEON で値をロードし、NEON->ARM 転送を実行すると、パイプライン フラッシュが発生し、11 ~ 14 サイクルが無駄になります。

最良の解決策は、関数を完全に手動アセンブリで作成することです。

ただし、型キャストと切り捨てなしで高速な float 比較を可能にする簡単なトリックがあります。

正のしきい値 (int 比較とまったく同じ速さ) :

void example(float * pSrc, float threshold, unsigned int count)
{
  typedef union {
    int ival,
    unsigned int uval,
    float fval
  } unitype;

  unitype v, t;
  if (count==0) return;
  t.fval = threshold;
  do {
    v.fval = *pSrc++;
    if (v.ival < t.ival) {
      // your code here
    }
    else {
      // your code here (optional)
    }
  } while (--count);
}

負のしきい値 (int 比較よりも値ごとに 1 サイクル多い):

void example(float * pSrc, float threshold, unsigned int count)
{
  typedef union {
    int ival,
    unsigned int uval,
    float fval
  } unitype;

  unitype v, t, temp;
  if (count==0) return;
  t.fval = threshold;
  t.uval &= 0x7fffffff;
  do {
    v.fval = *pSrc++;
    temp.uval = v.uval ^ 0x80000000;
    if (temp.ival >= t.ival) {
      // your code here
    }
    else {
      // your code here (optional)
    }
  } while (--count);
}

上記で受け入れられているものよりもかなり速いと思います。繰り返しますが、私は少し遅すぎます。

于 2012-05-02T07:49:02.103 に答える
0

丸め誤差が問題にならない場合は、std::lrintを使用する必要があります。

より高速な浮動小数点から整数への変換では、floatからintへの変換に使用することをお勧めします。

于 2012-04-30T10:30:40.287 に答える