-
-
Notifications
You must be signed in to change notification settings - Fork 116
Description
When I wrote the new memmem implementation earlier this year, one thing I did was write the implementation as something that was generic over the vector type:
memchr/src/memmem/genericsimd.rs
Lines 95 to 107 in 186ac04
/// # Safety | |
/// | |
/// Since this is meant to be used with vector functions, callers need to | |
/// specialize this inside of a function with a `target_feature` attribute. | |
/// Therefore, callers must ensure that whatever target feature is being used | |
/// supports the vector functions that this function is specialized for. (For | |
/// the specific vector functions used, see the Vector trait implementations.) | |
#[inline(always)] | |
pub(crate) unsafe fn fwd_find<V: Vector>( | |
fwd: &Forward, | |
haystack: &[u8], | |
needle: &[u8], | |
) -> Option<usize> { |
where an example of it being called, e.g. for AVX2, is:
Line 27 in 186ac04
genericsimd::Forward::new(ninfo, needle).map(Forward) |
So basically, the idea here is, you write the nasty SIMD code once, and then write some trivial shims for each target feature you want to support.
The actual use of SIMD in this crate is reasonably simple, so it turns out that the trait defining the API of a vector is quite small:
Lines 21 to 32 in 186ac04
pub(crate) trait Vector: Copy + core::fmt::Debug { | |
/// _mm_set1_epi8 or _mm256_set1_epi8 | |
unsafe fn splat(byte: u8) -> Self; | |
/// _mm_loadu_si128 or _mm256_loadu_si256 | |
unsafe fn load_unaligned(data: *const u8) -> Self; | |
/// _mm_movemask_epi8 or _mm256_movemask_epi8 | |
unsafe fn movemask(self) -> u32; | |
/// _mm_cmpeq_epi8 or _mm256_cmpeq_epi8 | |
unsafe fn cmpeq(self, vector2: Self) -> Self; | |
/// _mm_and_si128 or _mm256_and_si256 | |
unsafe fn and(self, vector2: Self) -> Self; | |
} |
OK, so what's this issue about? I think ideally, we would push the Vector
trait up a level in the module hierarchy, port the existing x86 SIMD memchr implementation to a "generic" version, and then replace the existing implementations with shims that call out to the generic version.
This will hopefully let us easily add a WASM implementation of memchr, but adding other implementations in the future would be good too once more intrinsics (e.g., for ARM) are added to std.
(One wonders whether we should just wait for portable SIMD to land in std, but I don't know when that will happen.)