[英]Finding MSB set of the maximal magnitude element in array
给定一个类型为signed short
的len
元素数组,可以找到数组中最大绝对值元素中设置的最高有效位的位置。 例如,如果数组L包含{-134, 123, 0, -890}
则f(L)
应返回floor(log2(abs(-890)))+1
。
这是我目前的功能:
short MSBSetMaxMagnitude(const short *p, int len)
{
unsigned int t = 0;
while (len > 0)
{
t |= abs(*p);
p++;
len--;
}
if(t)
return (short)(32 - __builtin_clz(t));
else
return 0;
}
但是,由于abs()函数需要分支,因此速度有点慢。 我尝试使用没有分支的abs(),但它甚至更慢,因为它包含至少3个算术指令。 所以我希望有一种有效的算法可以找到我需要的东西。
看到您在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个字),并使用一些比特来分隔它们。
任何现代处理器上的三个算术指令应该花费很少的时间。 在管理循环和索引时,您正在执行两个算术运算和一个条件分支。 缓慢可能是由于数据高速缓存未命中和由于指针使用和指针算法而导致编译器难以展开的循环的组合。
如果没有查看数组中的每个元素,就无法找到依赖于数组中每个元素的值,因此目标应该是确保整个事件在扫描数组所花费的时间内运行。
您可以通过替换来测试这是否是问题:
t |= abs(*p);
用t | = * p;
如果这不是很快,我建议在手动展开的循环中试验非分支abs版本。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.