2016-12-01 15 views
5

Tôi đang cố gắng tìm trường hợp đầu tiên của một ký tự, trong trường hợp này là "" sử dụng simd (AVX2 hoặc cũ hơn) .Tôi muốn sử dụng _mm256_cmpeq_epi8, nhưng sau đó tôi cần một cách nhanh chóng để tìm kiếm xem có bất kỳ byte kết quả nào trong __m256i đã được đặt thành 0xFF hay không. Kế hoạch sau đó sử dụng _mm256_movemask_epi8 để chuyển đổi kết quả từ byte thành bit và sử dụng ffs để có chỉ mục phù hợp. để di chuyển ra khỏi một phần tại một thời gian sử dụng _mm_movemask_epi8 Bất kỳ lời đề nghị khácTìm ví dụ đầu tiên của một ký tự bằng cách sử dụng simd

+0

Tôi nên thêm, simd là không cần thiết, nói chung tôi chỉ đang tìm cách tiếp cận nhanh nhất. Có lẽ một chút phép thuật? – Jimbo

+1

Ý tưởng cơ bản của bạn là âm thanh - Tôi có cảm giác rằng có thể đã có quá trình triển khai SIMD nhiều như bạn mô tả trong câu hỏi trước trên StackOverflow, nhưng tìm kiếm nhanh không bật lên. Lưu ý rằng những gì bạn đang thực hiện có hiệu quả là 'strchr' (hoặc' memchr' nếu bạn biết chiều dài), và có thể đã có các triển khai tối ưu hóa SIMD có sẵn này. Cũng lưu ý rằng đối với các chuỗi không có trong bộ đệm, chức năng của bạn cũng có thể bị giới hạn băng thông bộ nhớ. –

+1

[Đây là một thực thi SSE quét một chuỗi cho một ''\ 0''] (http://stackoverflow.com/a/14524319/253056) (hiệu quả' strlen'), mà bạn có thể có khả năng thích nghi. –

Trả lời

4

Bạn có ý tưởng đúng với _mm256_cmpeq_epi8 -?..>_mm256_movemask_epi8 AFAIK, đó là cách tối ưu để thực hiện điều này cho CPU Intel ít nhất PMOVMSKB r32, ymm là tốc độ tương tự như phiên bản 16-byte XMM, do đó, nó sẽ là một mất mát lớn để giải nén hai l anes của một vector 256b và di chuyển chúng một cách riêng biệt và sau đó kết hợp lại các kết quả số nguyên. (Nguồn:. Agner Fog's instruction table Xem các liên kết Perf khác trong thẻ wiki.)

Tận dụng mã bên trong vòng lặp như hiệu quả càng tốt bằng cách rời khỏi ffs cho đến sau khi bạn đã xác định một tổ chức phi zero kết quả từ _mm256_movemask_epi8.

KIỂM TRA/JCC có thể làm cầu chì vĩ mô thành một uop đơn, nhưng BSF/JCC thì không, do đó, nó có thêm hướng dẫn. (Và bạn sẽ khó có được trình biên dịch C để phát ra BSF/JCC. Khả năng phân nhánh trên kết quả của ffs sẽ cung cấp cho bạn một số loại kiểm tra cho đầu vào là khác 0, sau đó thêm BSF, sau đó thêm 1 Sau đó, lưu ý rằng đối với các vấn đề tương tự, so sánh movemask (ví dụ để kiểm tra xem nó là 0xFFFFFFFF) có hiệu quả như phân nhánh trên nó hay không. không khác.


Như Paul R đã đề xuất, xem xét một số triển khai strlen, strchr và memchr có thể mang tính thông tin. Có nhiều triển khai asm viết tay trong triển khai libc nguồn mở và các địa điểm khác. (ví dụ glibc, và Agner Fog's asmlib.)

Nhiều phiên bản của glibc quét lên ranh giới liên kết, sau đó sử dụng vòng lặp không đọc 64B tại một thời điểm (trong 4 vector SSE, vì tôi không nghĩ rằng glibc có AVX2 phiên bản).

Để tối ưu hóa chuỗi dài, hãy giảm chi phí từ việc kiểm tra kết quả so sánh bằng cách HOẶC so sánh kết quả với nhau và kiểm tra điều đó. Nếu bạn tìm thấy một lần truy cập, hãy quay lại và kiểm tra lại vectơ của bạn để xem véc tơ nào có lượt truy cập.

Có thể sẽ hiệu quả hơn khi thực hiện ffs trên một số nguyên 64 bit mà bạn đã tạo ra từ nhiều kết quả di chuyển (với ca và |). Tôi không chắc chắn về việc làm điều này bên trong vòng lặp trước khi thử nghiệm cho số không; Tôi không nhớ nếu một trong những chiến lược strlen của glibc đã làm điều đó hay không.


Mọi thứ tôi đã đề xuất ở đây là có thể thấy trong asm trong các chiến lược glibc khác nhau cho strlen, memchr và các chức năng liên quan. Đây là sysdeps/x86_64/strlen.S, nhưng tôi có thể có một tệp nguồn khác ở đâu đó bằng cách sử dụng hơn SSE2 cơ bản. . (Hoặc không, tôi có thể nghĩ đến việc một chức năng khác nhau, có lẽ không có gì để đạt được vượt SSE2, cho đến khi AVX (insns 3 toán hạng) và AVX2 (256b vectơ số nguyên) là

Xem thêm:

  • glibc's strchr-avx2.S (Woboq.org có một trình duyệt nguồn tốt với tìm kiếm hữu ích cho tên tập tin/biểu tượng).
  • glibc của memchr-avx2.S

glibc's memchr sử dụng PMAXUB thay vì POR. Tôi không chắc chắn nếu đó là hữu ích cho một số lý do vi kiến ​​trúc phức tạp, nhưng nó chạy trên cổng ít hơn trên hầu hết các CPU. Có lẽ đó là mong muốn, để tránh xung đột tài nguyên với cái gì khác? IDK, có vẻ lạ, vì nó cạnh tranh với PCMPEQB.

+0

Suy nghĩ đằng sau _mm_movemask_epi8 là có vẻ như nó nhanh hơn trên mới hơn bộ vi xử lý hơn _mm256_movemask_epi8, ngay cả khi nó cần được gọi hai lần. Nếu không, bạn sẽ nhận được một khoản tiết kiệm để tránh cuộc gọi thêm. Điều này tất nhiên dường như là bộ xử lý phụ thuộc, vì vậy trên Haswell, nơi họ có độ trễ bằng nhau, các cuộc gọi lớn hơn (tức là _mm256_movemask_epi8) có vẻ là một cách tiếp cận tốt hơn. – Jimbo

+0

@Jimbo: oh hmm, tôi đã không nhận thấy rằng 'PMOVMSKB r, v' trong bảng Agner Fog cho Skylake được liệt kê là độ trễ 2-3c. Trên Haswell, 'VMOVMSKPS/D r32, ymm' là độ trễ 2c, nhưng phiên bản xmm là độ trễ 3c! Thật bất ngờ. Bạn thấy phiên bản 256b ở đâu chậm hơn? Bạn có chắc chắn phiên bản ymm không nhanh hơn trên Skylake không? –

+0

@Jimbo: Dù sao, sự khác biệt là nhiều nhất một chu kỳ độ trễ và không có thêm uops hoặc thông lượng. ** '_mm256_movemask_epi8' vẫn là cách tốt nhất bạn có thể làm **. Không có gì bạn có thể làm với hai nửa riêng biệt có thể có thể tốt như chỉ sử dụng một VPMOVMSKB r32, ymm. Sử dụng một movmsk 128b trên làn đường trên sẽ yêu cầu giải nén nó trước tiên đến 128b thấp của một thanh ghi, với một chu kỳ 3-chu kỳ làn đường ngang shuffle như VEXTRACTF128. –

Các vấn đề liên quan