C 在一个位范围内查找第一个设置位的位置
在我的代码中,我发现处理器将大部分时间花在下面所示的函数上。循环的目标是找出满足循环内条件的val1值。变量Val1和a的类型为long int(64位)。而且,它们是函数内部声明的局部非静态变量C 在一个位范围内查找第一个设置位的位置,c,optimization,bit-manipulation,simd,fixed-point,C,Optimization,Bit Manipulation,Simd,Fixed Point,在我的代码中,我发现处理器将大部分时间花在下面所示的函数上。循环的目标是找出满足循环内条件的val1值。变量Val1和a的类型为long int(64位)。而且,它们是函数内部声明的局部非静态变量 long long int findval(long long int x) { long long int Val1,a=x; for (Val1 = 63; Val1 > 22; Val1--) { if (((a >> Val1) &
long long int findval(long long int x)
{
long long int Val1,a=x;
for (Val1 = 63; Val1 > 22; Val1--)
{
if (((a >> Val1) & 1) == 1)
break;
}
return Val1;
}
有没有其他简单/优化的方法来找出Val1值?首先,请记住,仅仅因为处理器在该函数片段上花费了大部分时间,并不意味着该片段存在问题。也许你应该试着找出为什么你的代码经常调用这个片段 第二,既然你是来寻求帮助的,你不妨向我们展示你所拥有的一切,而不是你所拥有的一部分,你认为这些应该足以让我们找出问题所在。最重要的是,您真的应该准确地向我们展示变量是如何声明的,以及它们声明的确切位置。它们是本地的吗?它们是静态的吗?可能是您将某个东西声明为
volatile
?没有什么是无关紧要的,一切都重要
在任何情况下,如果我们假设代码段可以优化,那么我会说:
您的Val1
不应为long-long-int
,因为其值仅在23到63之间。因此,它应该是一个int
(如果出于某种原因,必须将Val1
计算为长整型
,则尝试将其强制转换为循环之前类型为int
的另一个变量,并在循环中使用该变量。)
如果您尝试这样做,那么编译器可能会发现您要做的是找到位范围内的第一个非零位,并用一条机器指令替换整个循环。首先,请记住,仅仅因为您发现处理器在该函数片段上花费了大部分时间,并不意味着该片段存在问题。也许你应该试着找出为什么你的代码经常调用这个片段 第二,既然你是来寻求帮助的,你不妨向我们展示你所拥有的一切,而不是你所拥有的一部分,你认为这些应该足以让我们找出问题所在。最重要的是,您真的应该准确地向我们展示变量是如何声明的,以及它们声明的确切位置。它们是本地的吗?它们是静态的吗?可能是您将某个东西声明为
volatile
?没有什么是无关紧要的,一切都重要
在任何情况下,如果我们假设代码段可以优化,那么我会说:
您的Val1
不应为long-long-int
,因为其值仅在23到63之间。因此,它应该是一个int
(如果出于某种原因,必须将Val1
计算为长整型
,则尝试将其强制转换为循环之前类型为int
的另一个变量,并在循环中使用该变量。)
如果您这样做,编译器可能会发现您要做的是找到位范围内的第一个非零位,并用一条机器指令替换整个循环。警告:我的答案写错了(第一位在右边),对不起。无论如何,这些方法可以很容易地适应MSb
您可以通过查阅表格的方式缩短流程。您可以为从
0
到2^k-1
的所有数字预计算最右边位的索引。您将一次处理k
位的切片中的数字,然后从右到左尝试切片,直到切片为非零
一个有趣的选择是将long映射到一个8字节的数组;字节对应于256个条目的查找表。这样,您就可以从按字节直接寻址中获益
也可以通过shorts
进行处理,代价是LUT为65536(64K)个条目。最佳选择可能介于两者之间。有缓存效果
另一个有用的方法是二分法:屏蔽32个高阶位(或加载低
int
)并测试零。然后对于非零部分,屏蔽掉16个高阶位,依此类推。最后,使用LUT技巧。只需3个步骤,您就可以从64个减少到8个
如果位索引的分布是均匀的,则这是合适的。如果偏向于较小的值,顺序搜索可能会更好。警告:我的答案写错了(第一位在右边),对不起。无论如何,这些方法可以很容易地适应MSb
您可以通过查阅表格的方式缩短流程。您可以为从
0
到2^k-1
的所有数字预计算最右边位的索引。您将一次处理k
位的切片中的数字,然后从右到左尝试切片,直到切片为非零
一个有趣的选择是将long映射到一个8字节的数组;字节对应于256个条目的查找表。这样,您就可以从按字节直接寻址中获益
也可以通过shorts
进行处理,代价是LUT为65536(64K)个条目。最佳选择可能介于两者之间。有缓存效果
另一个有用的方法是二分法:屏蔽32个高阶位(或加载低
int
)并测试零。然后对于非零部分,屏蔽掉16个高阶位,依此类推。最后,使用LUT技巧。只需3个步骤,您就可以减少
#include <limits.h>
inline
int MSB_position_clamped (long long x)
{
int maxpos = CHAR_BIT * sizeof(x) - 1;
x |= 1LL << 22; // avoid x==0 UB and make clz at least 22
return maxpos - __builtin_clzll(x);
}
# clang 9.0 -O3 for x86-64 System V
MSB_position_clamped:
or rdi, 4194304
bsr rax, rdi
ret