Nếu AVX2 là chấp nhận được (với các phiên bản trước đó nó đã không làm việc ra rất tốt, nhưng bạn vẫn có thể làm điều gì đó ở đó), bạn có thể tìm kiếm ở rất nhiều địa điểm cùng một lúc.Tôi không thể kiểm tra điều này trên máy của tôi (chỉ biên dịch) vì vậy sau đây là nhiều hơn để cung cấp cho bạn một ý tưởng về cách nó có thể được tiếp cận hơn sao chép & dán mã, vì vậy tôi sẽ cố gắng giải thích nó hơn là chỉ mã-dump .
Ý tưởng chính là đọc uint64_t
, dịch chuyển sang phải bởi tất cả các giá trị có ý nghĩa (0 đến 7), sau đó cho mỗi 8 giá trị mới uint64_t
, kiểm tra xem byte có ở đó không. Biến chứng nhỏ: đối với sự thay đổi của uint64_t
nhiều hơn 0, vị trí cao nhất sẽ không được tính vì nó có các số 0 được chuyển vào trong đó có thể không có trong dữ liệu thực tế. Một khi điều này được thực hiện, các uint64_t
tiếp theo nên được đọc tại một bù đắp của 7 từ hiện tại, nếu không có một ranh giới không được kiểm tra trên. Đó là tốt mặc dù, tải trọng không phải là xấu như vậy nữa, đặc biệt là nếu họ không rộng.
Vì vậy, ngay bây giờ cho một số (chưa được kiểm tra, và không đầy đủ, xem dưới đây) mã,
__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);
}
}
Các funny "chút-index và byte-index đang hoán đổi" Vấn đề là vì tìm kiếm trong một qword được thực hiện byte bởi byte và kết quả của những so sánh đó kết thúc trong 8 bit liền kề, trong khi tìm kiếm "shifted by 1" kết thúc trong 8 bit tiếp theo và cứ tiếp tục như vậy. Vì vậy, trong các mặt nạ kết quả, chỉ mục của byte chứa 1 là bit-offset, nhưng bit-index trong byte đó thực sự là byte-offset, ví dụ 0x8000 sẽ tương ứng với việc tìm byte tại byte thứ 7 của qword đã được dịch chuyển sang phải 1, vì vậy chỉ số thực tế là 8 * 7 + 1.
Ngoài ra còn có vấn đề về "đuôi", phần dữ liệu còn lại khi tất cả các khối 7 byte đã được xử lý. Nó có thể được thực hiện nhiều theo cùng một cách, nhưng bây giờ nhiều vị trí hơn chứa các byte không có thật. Bây giờ n - i
byte bị bỏ lại, vì vậy mặt nạ phải có n - i
bit được đặt trong byte thấp nhất và ít hơn cho tất cả các byte khác (vì lý do tương tự như trước đó, các vị trí khác có số 0 được chuyển vào). Ngoài ra, nếu có chính xác 1 byte "trái", nó không thực sự còn lại bởi vì nó đã được thử nghiệm rồi, nhưng điều đó không thực sự quan trọng. Tôi sẽ giả sử dữ liệu đủ đệm để truy cập ngoài ranh giới không quan trọng. Tại đây, chưa được kiểm tra:
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);
}
}
Điều này khó thực hiện trong c được xác định rõ. Bạn không thể giả định có 8 bit trong một byte. Tôi muốn bị cám dỗ để sử dụng một giải pháp dựa trên lắp ráp. – Bathsheba
Có lẽ bạn có thể tìm thấy một số cảm hứng [ở đây] (http://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm#Shifting_substrings_search_and_competing_algorithms). Nó không hoàn toàn giống nhau, nhưng về mặt khái niệm thì tương tự. – mkrieger1
Có thể tìm thấy các mẫu bit chồng chéo không? Tôi đề nghị chuyển 'dữ liệu' và' tìm kiếm' thành chuỗi (một byte cho mỗi bit) và sử dụng 'ptr = strstr (lastptr + 1, search)' hoặc 'ptr = strstr (lastptr + 8, search)' –