给定类型为len
的{{1}}元素数组,它是在数组中的最大绝对值元素中找到最高有效位的位置。例如,如果数组L包含signed short
,则{-134, 123, 0, -890}
应返回f(L)
。
这是我目前的职能:
floor(log2(abs(-890)))+1
然而,由于abs()函数需要分支,因此有点慢。我尝试使用没有分支的abs(),但它甚至更慢,因为它包含至少3个算术指令。所以我希望有一种有效的算法可以找到我需要的东西。
答案 0 :(得分:1)
看到您在ARM平台上工作,您可以在2条指令中使用abs
的以下实现:
EORS r1, r1, r1, ASR #32 (x = x ^ (x >> 32); carry_flag = sign_bit)
ADC r1, r1, #0 (add the sign_bit to x)
如果在计算中可以容忍+/- 1的误差,则丢弃第二条指令;那么,你可以用C表达它:
int abs_almost_exact(int x)
{
return x ^ (x >> 32);
}
但是,更大的问题是循环。您可能会从展开中获益很多(因为每次迭代都没那么做):
do { // assuming len is even!
int value1 = *p++;
int value2 = *p++;
value1 = abs(value1); // or replace abs by the hand-made version
value2 = abs(value2);
t |= value1;
t |= value2;
len--;
}
while (len > 0);
注意:我将while {}
替换为do {} while
,因为我使用的编译器(ARM编译器)以这种方式生成更好的代码。
请注意,从内存(在我使用的处理器上)加载short
变量时,ARM有2个时钟周期的延迟。因此,最小展开因子是3(但你应该尽可能多地展开)。
哦,您的处理器是否支持从内存中读取short
(半字)变量?我听说过一些非常古老的处理器无法做到这一点。如果你是这种情况,你应该改变代码一次加载2个值(1个字),然后使用一些比特来分隔它们。
答案 1 :(得分:0)
三个算术指令在任何现代处理器上都应该花费很少的时间。在管理循环和索引时,您正在执行两个算术运算和一个条件分支。缓慢可能是由于数据高速缓存未命中和由于指针使用和指针算法而导致编译器难以展开的循环的组合。
在没有查看数组中的每个元素的情况下,无法找到依赖于数组中每个元素的值,因此目标应该是确保整个事件在扫描数组所花费的时间内运行。
您可以通过替换以下内容来测试这是否是问题:
t |= abs(*p);
与 t | = * p;
如果这不是快得多,我建议在手动展开的循环中试验非分支abs版本。