通常,字符串搜索算法(如Boyer-Moore)针对搜索字符串为long-ish的情况进行了优化。也就是说,Boyer-Moore很棒,因为通过使用我们的文本排列搜索字符串,如果搜索字符串的末尾与文本不匹配,我们可以跳过N = len(search string)
个字符。
但是如果我们的搜索字符串真的很短怎么办?像一个字节或字符?在这种情况下,Boyer-Moore并没有多大帮助。
那么,有哪些替代算法可以加速搜索?
我知道许多优化的库搜索例程(如C中的memchr
)采取逐字逐句读取输入字符串的策略,而不是char字符。因此,在64位机器上,可以同时检查8个字节,而不是单个字节。
我想知道这些优化的字符串/字节搜索实际上是如何工作的。那么实际比较是如何工作的呢?我知道它显然必须涉及位屏蔽 - 但我不知道如何执行所有位屏蔽比仅仅逐字符搜索更好。
因此,假设我们的搜索字符为0xFF
。忽略对齐问题,我们假设我们有一些输入缓冲区:void* buf
。我们可以通过以下方式逐字阅读:
const unsigned char search_char = 0xFF;
unsigned char* bufptr = static_cast<unsigned char*>(buf);
unsigned char* bufend = bufptr + BUF_SIZE;
while (bufptr != bufend)
{
// Ignore alignment concerns for now, assume BUF_SIZE % sizeof(uintptr_t) == 0
//
std::uinptr_t next_word = *reinterpret_cast<std::uintptr_t*>(bufptr);
// ... but how do we compare next_word with our search char?
bufptr += sizeof(std::uintptr_t);
}
我也意识到上面的代码不是严格可移植的,因为std::uintptr_t
并不能保证实际上是字大小。但是,为了这个问题,我们假设std::uinptr_t
等于处理器字大小。 (实际实现可能需要特定于平台的宏来获取实际的字大小)
那么,我们如何实际检查字节0xFF
是否出现在next_word
的值的任何位置?
我们当然可以使用OR
操作,但似乎我们仍然需要执行大量的OR和位移以检查next_word
的每个字节,对于这种优化实际上是否比简单地逐字符扫描更有意义,这一点值得怀疑。
答案 0 :(得分:2)
您可以使用this snippet from Bit Twiddling Hacks:
#define haszero(v) (((v) - 0x01010101UL) & ~(v) & 0x80808080UL)
#define hasvalue(x,n) \
(haszero((x) ^ (~0UL/255 * (n))))
它有效地将每个字节与要测试的字符进行异或,然后确定是否有任何字节现在为零。
此时,您可以从表达式的返回值中获取匹配字节(或字节)的位置,例如:如果最低有效字节与值匹配,则该值将为0x00000080。