我有位图
uint64_t bitmap[10000]
跟踪系统中分配的资源。 现在的问题是如何有效地找到这个位图中的第一个未设置(零)位?
我知道glibc中有ffsll(unsigned long long)
用于查找第一个设置位,我假设使用硬件指令来执行此操作。
要在我的情况下使用此函数,首先我需要初始化数组以将每个位设置为1,然后当我进行资源分配时,我必须线性搜索数组以获得第一个无零字。然后使用ffsll()查找第一个设置位。
我怎样才能更快地完成?
更新: 我在x86-64 cpu上。
答案 0 :(得分:4)
您可以维护位图的树以有效地找到最低位集。在64位CPU上,只需要树的深度为3即可跟踪4096个64位元素 - 这意味着只能使用三个ffsll
个调用。
基本上,这可以通过将数组划分为64个字块并为每个块分配一个64位索引来实现。如果相应的位集字已设置所有位,则设置索引字的一位。更改位集中的位时,可以调整相应的索引字。
然后,您可以在顶部构建另一个索引数组以形成树。
每个位更改都需要一些额外的工作,但与您在需要空闲位时无需线性搜索bitset所节省的成本相比,额外工作(和存储)的总量可以忽略不计。
答案 1 :(得分:0)
我不确定你的速度会比这快得多,但我很容易被证明是错的:
uint64_t bitmap[10000];
unsigned int i;
for (i = 0; i < (sizeof(bitmap) / sizeof(*bitmap)) && 0xFFFFFFFFFFFFFFFF == bitmap[i]; ++i);
const int bitInWord = ffsll(bitmap[i]);
const unsigned int firstZeroBit = bitInWord ? i * sizeof(*bitmap) * CHAR_BIT + bitInWord : 0;
答案 2 :(得分:0)
如果您使用的是32位cpu,那么您不希望这样做。而是使用32位整数的数组。数组上的循环会更快。
此外,您可以对每个值进行1个字节的编码,然后预先存储该字节中设置的第一个位。因此,当您找到一个不是全部为0xFFFFFFFF的整数时,您可以简单地比较这些字节。如果第一个字节不是0xFF,那么它就在那个字节中,依此类推。
因此,如果一个字节不是0xFF,则意味着它是该字节的255个可能值之一。并且每个值都设置了第一位。
另一种解决问题的方法是尽可能将其分成块。我不知道你的资源是什么,所以我不能说。
还要考虑在前一次扫描的情况下,它将循环前进的未设置位。如果存储先前结果的索引,则可以在下一次搜索时从同一索引开始。将此索引称为pos并每次使用它。
每次将某个位设置为零时,也可以创建一个小的“自由”索引数组,因此当“pos”到达数组的末尾时,只需从其中一个保存的索引中重新开始。
换句话说,你真的不希望每次都运行这么长的循环。这是你的问题,而不是获得该位的最终指令。使用如上所述的索引跟踪,它将快几百倍。