扫描位数组以查找多个位的模式

Scanning bit array for pattern of multiple bits

本文关键字:模式 查找 数组 扫描      更新时间:2023-10-16

我正在编写一个由位图(uint8_t数组)支持的内存分配器,当前当分配请求出现时,我扫描从 0 到 n 位的位图顺序并搜索可以满足请求的空间。(位 1 表示页面使用 0 表示页面是免费的)现在,与其一次搜索一位空间,不如有技术可以更快地扫描整个阵列?即,如果收到对 3 页内存的请求,我想一次性搜索数组中的000模式,理想情况下无需循环?

PS:我没有使用std::bitset因为它不适用于我正在使用的编译器。AFAIK 也不允许我搜索多个位。

编辑:位被打包成一个字节,uint8_t有8页(每比特1个)编码。

要扫描一个空白页,您可以一次循环一个完整的字节遍历位数组,并检查它是否小于 255。如果它较小,则至少有一个零位。更好的方法是一次扫描 32 或 64 位(无符号整数),然后在 uint 内缩小搜索范围。

为了优化一点,您可以跟踪具有零位的第一个字节(并在释放页面时更新该位置)。一旦您分配了该免费页面,这可能会产生误报,但至少下次扫描可以从那里开始而不是从头开始。

如果您愿意以 2 的幂对齐较大的块(具体取决于您的数据结构),则可以优化对多个页面的扫描。例如,要分配 8 页,您只需扫描一个完整的字节为零:

1 page: scan for any zero bit (up to 64 bits at a time)
2 pages: scan for 2 zero bits at bit position 0,2,4,6
3-4 pages: scan for zero nibble (for 3 pages, the fourth would be available for 1 page then)
5-8 pages: scan for an empty byte
for each of the above, you could first scan 64 bits at a time.

这样,您就不必担心(或检查)byte/uint32/uint64 边界处的重叠零范围。

对于每种类型,可以保留/更新具有第一个空闲块的起始位置。

不是您问题的完整答案(我想),但我希望以下函数可以提供帮助。

template <typename I>
bool scan_n_zeros (I iVal, std::size_t num)
{
while ( --num )
iVal |= ((iVal << 1) | I{1});
return iVal != I(-1);
}

它返回(如果我写得正确)true如果(不是在哪里)iVal中至少有num个连续的零位.

以下是Tuint8_t时的完整工作示例

#include <iostream>
template <typename I>
bool scan_n_zeros (I iVal, std::size_t num)
{
while ( --num )
iVal |= ((iVal << 1) | I{1});
return iVal != I(-1);
}
int main()
{
uint8_t  u0 { 0b00100100 };
uint8_t  u1 { 0b00001111 };
uint8_t  u2 { 0b10000111 };
uint8_t  u3 { 0b11000011 };
uint8_t  u4 { 0b11100001 };
uint8_t  u5 { 0b11110000 };
std::cout << scan_n_zeros(u0, 2U) << std::endl; // print 1
std::cout << scan_n_zeros(u0, 3U) << std::endl; // print 0
std::cout << scan_n_zeros(u1, 4U) << std::endl; // print 1
std::cout << scan_n_zeros(u1, 5U) << std::endl; // print 0
std::cout << scan_n_zeros(u2, 4U) << std::endl; // print 1
std::cout << scan_n_zeros(u2, 5U) << std::endl; // print 0
std::cout << scan_n_zeros(u3, 4U) << std::endl; // print 1
std::cout << scan_n_zeros(u3, 5U) << std::endl; // print 0
std::cout << scan_n_zeros(u4, 4U) << std::endl; // print 1
std::cout << scan_n_zeros(u4, 5U) << std::endl; // print 0
std::cout << scan_n_zeros(u5, 4U) << std::endl; // print 1
std::cout << scan_n_zeros(u5, 5U) << std::endl; // print 0
}