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

如果浮点数是 32 位 IEEE-754 而整数也是 32 位,并且如果没有 +infinity、-infinity 和NaN值,我们可以通过一个小技巧将浮点数与整数进行比较:

#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 arr[10000];
float threshold;
....

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

否则您将进行昂贵的浮点整数转换。

于 2012-04-30T10:31:23.377 回答
2

您的示例显示了编译器生成的代码有多糟糕:

它使用 NEON 加载一个值,只是为了将其转换为 int,然后执行 NEON->ARM 传输,这会导致管道刷新,从而浪费 11~14 个周期。

最好的解决方案是完全手工组装编写函数。

但是,有一个简单的技巧可以在不进行类型转换和截断的情况下进行快速浮点比较:

阈值为正(与 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

Faster Floating Point to Integer Conversions建议将其用于浮点到 int 的转换。

于 2012-04-30T10:30:40.287 回答