如果AVX2是可以接受的(與早期版本中,它沒有發揮出來如此好,但你仍然可以在那裏做點什麼),你可以同時在很多地方搜索。我無法在我的機器上進行測試(僅編譯),因此以下內容更多的是給你一個關於如何接近它的想法,而不是複製&粘貼代碼,所以我會試着解釋它,而不僅僅是代碼轉儲。
其主要思想是讀取一個uint64_t
,通過所有有意義的值(0到7)將其右移,然後對於這8個新的uint64_t
中的每一個,測試該字節是否在那裏。小併發症:對於uint64_t
的偏移超過0,最高位置不應該被計數,因爲它有零位移入其中,可能不在實際數據中。一旦完成,下一個uint64_t
應該在與當前偏移量相差7的位置處讀取,否則會有一個未檢查的邊界。這很好,但未對齊的負載不再那麼糟糕,特別是如果它們不寬的話。
所以現在對一些(未經測試,和不完整的,見下文)的代碼,
__m256i needle = _mm256_set1_epi8(find);
size_t i;
for (i = 0; i < n - 6; i += 7) {
// unaligned load here, but that's OK
uint64_t d = *(uint64_t*)(data + i);
__m256i x = _mm256_set1_epi64x(d);
__m256i low = _mm256_srlv_epi64(x, _mm256_set_epi64x(3, 2, 1, 0));
__m256i high = _mm256_srlv_epi64(x, _mm256_set_epi64x(7, 6, 5, 4));
low = _mm256_cmpeq_epi8(low, needle);
high = _mm256_cmpeq_epi8(high, needle);
// in the qword right-shifted by 0, all positions are valid
// otherwise, the top position corresponds to an incomplete byte
uint32_t lowmask = 0x7f7f7fffu & _mm256_movemask_epi8(low);
uint32_t highmask = 0x7f7f7f7fu & _mm256_movemask_epi8(high);
uint64_t mask = lowmask | ((uint64_t)highmask << 32);
if (mask) {
int bitindex = __builtin_ffsl(mask);
// the bit-index and byte-index are swapped
return 8 * (i + (bitindex & 7)) + (bitindex >> 3);
}
}
有趣「位索引和字節索引被交換」的事情是因爲四字內搜索是通過完成字節字節,並且這些比較的結果以8個相鄰位結束,而對於「移位1」的搜索結束於接下來的8個位中,依此類推。因此,在生成的掩碼中,包含1的字節索引是一個位偏移量,但該字節內的位索引實際上是字節偏移量,例如0x8000將對應於查找第7個字節的字節被右移1的qword,所以實際的索引是8 * 7 + 1。
還有一個「尾部」的問題,即所有7個字節的塊已經被處理時遺留的部分數據。它可以以相同的方式完成,但現在更多的位置包含假字節。現在剩下n - i
個字節,所以掩碼必須在最低字節中設置n - i
位,並且對於所有其他字節(由於與之前相同的原因,其他位置的位移爲零),一個字節要少一個。另外,如果確切地有1個字節「左」,它不會真的留下,因爲它已經被測試過了,但這並不重要。我會假設數據足夠填充以便訪問出界不重要。這是未經測試:
if (i < n - 1) {
// make n-i-1 bits, then copy them to every byte
uint32_t validh = ((1u << (n - i - 1)) - 1) * 0x01010101;
// the lowest position has an extra valid bit, set lowest zero
uint32_t validl = (validh + 1) | validh;
uint64_t d = *(uint64_t*)(data + i);
__m256i x = _mm256_set1_epi64x(d);
__m256i low = _mm256_srlv_epi64(x, _mm256_set_epi64x(3, 2, 1, 0));
__m256i high = _mm256_srlv_epi64(x, _mm256_set_epi64x(7, 6, 5, 4));
low = _mm256_cmpeq_epi8(low, needle);
high = _mm256_cmpeq_epi8(high, needle);
uint32_t lowmask = validl & _mm256_movemask_epi8(low);
uint32_t highmask = validh & _mm256_movemask_epi8(high);
uint64_t mask = lowmask | ((uint64_t)highmask << 32);
if (mask) {
int bitindex = __builtin_ffsl(mask);
return 8 * (i + (bitindex & 7)) + (bitindex >> 3);
}
}
這在定義明確的c中很難做到。你不能假定一個字節有8位。我會試圖使用基於程序集的解決方案。 – Bathsheba
也許你可以在這裏找到一些靈感(http://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm#Shifting_substrings_search_and_competing_algorithms)。這不完全相同,但在概念上相似。 – mkrieger1
重疊位模式是否可以找到?我建議將'data'和'search'轉換爲字符串(每個字節一個字節)並使用'ptr = strstr(lastptr + 1,search)'或'ptr = strstr(lastptr + 8,search)' –