是否有一些速度相当快的代码可以帮助我快速搜索大位图(几兆字节)中连续零位或一位的运行?
我所说的"相当快"是指可以利用机器单词大小,一次比较整个单词,而不是进行速度慢得可怕的逐位分析(比如vector<bool>
)。
它非常有用,例如搜索卷的位图以获得可用空间(用于碎片整理等)。
Windows有一个RTL_BITMAP
数据结构,可以与其API一起使用。
但我前段时间需要代码,所以我在这里写了它(警告,它有点难看):
https://gist.github.com/3206128
我只对进行了部分测试,所以它可能仍然有错误(尤其是在reverse
上)。但最近的版本(与本版本略有不同)似乎对我有用,所以值得一试。
整个过程的基本操作是能够快速找到一段比特的长度:
long long GetRunLength(
const void *const pBitmap, unsigned long long nBitmapBits,
long long startInclusive, long long endExclusive,
const bool reverse, /*out*/ bool *pBit);
考虑到它的多功能性,其他一切都应该很容易建立在这个基础上。
我尝试包含一些SSE代码,但它并没有显著提高性能。然而,一般来说,代码比逐点分析快很多倍,所以我认为它可能很有用。
测试你是否能以某种方式获得vector<bool>
的缓冲区应该很容易——如果你使用的是Visual C++,那么我提供了一个函数可以帮你做到这一点。如果你发现虫子,请随时告诉我。
我不知道如何直接处理内存字,所以我想出了一个处理字节的快速解决方案;为了方便起见,让我们画出一个计算连续数的算法:
构造两个大小为256的表,在其中为0和255之间的每个数字写入,即字节开头和结尾的尾随1的数量。例如,对于数字167(二进制为10100111),将1放在第一个表中,将3放在第二个表中。让我们称第一张表为BBeg,第二张表为BEnd。然后,对于每个字节b,有两种情况:如果它是255,那么在当前连续的一组一的当前和上加8,就处于一个一的区域中。否则,您将用BBeg[b]位结束一个区域,并用BEnd[b]位开始一个新区域。根据你想要的信息,你可以调整这个算法(这就是为什么我不在这里放任何代码的原因,我不知道你想要什么输出)。
一个缺陷是它不计算一个字节内的(小的)连续一组1。。。
除了这个算法,一个朋友告诉我,如果是磁盘压缩,只需查找0(空磁盘区域)和255(满磁盘区域)之间的字节。这是一种快速的启发式方法,可以构建一个需要压缩哪些块的映射。也许这超出了这个话题的范围。。。
听起来这可能很有用:
http://www.aggregate.org/MAGIC/#Population%20Count%20%28Ones%20Count%29和http://www.aggregate.org/MAGIC/#Leading%20Zero%20Count
你没有说你是否想做某种RLE,或者只是在字节中计数零和一位(比如0b1001应该返回1x1 2x0 1x1)。
查找表加上快速检查的SWAR算法可能会很容易地为您提供这些信息。有点像这样:
byte lut[0x10000] = { /* see below */ };
for (uint * word = words; word < words + bitmapSize; word++) {
if (word == 0 || word == (uint)-1) // Fast bailout
{
// Do what you want if all 0 or all 1
}
byte hiVal = lut[*word >> 16], loVal = lut[*word & 0xFFFF];
// Do what you want with hiVal and loVal
LUT必须根据您的预期算法进行构建。如果你想计算单词中连续的0和1的数量,你可以这样构建:
for (int i = 0; i < sizeof(lut); i++)
lut[i] = countContiguousZero(i); // Or countContiguousOne(i)
// The implementation of countContiguousZero can be slow, you don't care
// The result of the function should return the largest number of contiguous zero (0 to 15, using the 4 low bits of the byte, and might return the position of the run in the 4 high bits of the byte
// Since you've already dismissed word = 0, you don't need the 16 contiguous zero case.