【问题标题】:Scanning bit array for pattern of multiple bits扫描多位模式的位阵列
【发布时间】:2017-10-20 12:41:39
【问题描述】:

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

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

编辑:位被打包到一个字节中,uint8_t 有 8 个页面(每位 1 个)在其中编码。

【问题讨论】:

    标签: c++ c++11 memory-management bit-manipulation dynamic-allocation


    【解决方案1】:

    要扫描一个空页,您可以一次循环一个完整字节的位数组并检查它是否小于 255。如果小于 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.
    

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

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

    【讨论】:

      【解决方案2】:

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

      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
       }
      

      【讨论】:

      • 这个工作形式 uint16 所以我可以将两个字节分组到 uint16 中以解释从字节到下一个字节的转换,然后将窗口从开始到结束滑动一个字节?
      • @HamzaYerlikaya - 我开发了一个模板函数正是因为它旨在处理每个整数无符号类型
      • 你能稍微扩展一下位移的工作原理吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多