Add trait bounds on SimdBase::Element - #302
Conversation
439a50b to
c297a92
Compare
|
As I try to hack up my bitpacking code with this new support: one annoyance is that I hoped that I don't know enough deep Rust syntax to know if there's a way to express what I want here, short of defining a separate |
|
For reference, the code sample I gave in #299, updated for this PR and which now compiles: #[inline(always)]
fn unpack_aligned<S: Simd, T: SimdInt<S>>(
simd: S,
mut w: T,
bits_per_element: usize,
reference: T::Element,
out: &mut [T::Element],
) where
T::Element: SimdIntElement,
{
let mask = (T::Element::one() << bits_per_element) - T::Element::one();
let count = T::Element::BITS / bits_per_element;
for i in 0..count {
((w & mask) + reference).store_slice(&mut out[i * T::N..(i + 1) * T::N]);
w >>= bits_per_element as u32;
}
} |
|
I'll take a closer look later. The A trait bound I think is missing is |
|
I dumped all trait impls from u8 and f32 and this is what I got: impl Add<T> for T
impl Add<&T> for T
impl AddAssign<T> for T
impl AddAssign<&T> for T
impl Sub<T> for T
impl Sub<&T> for T
impl SubAssign<T> for T
impl SubAssign<&T> for T
impl Mul<T> for T
impl Mul<&T> for T
impl MulAssign<T> for T
impl MulAssign<&T> for T
impl Div<T> for T
impl Div<&T> for T
impl DivAssign<T> for T
impl DivAssign<&T> for T
impl Rem<T> for T
impl Rem<&T> for T
impl RemAssign<T> for T
impl RemAssign<&T> for TA bunch of these are nightly-only, but things like Default look worth adding to me. |
52193ee to
d5d9f4e
Compare
|
Good call, I was focused on just what num-traits provides but there's more good stuff in core! Technically with num-traits enabled you can get default-equivalent behavior with SimdElement::zero(), but it's much less obvious than Default::default(). I added bounds for stuff that is available in I can probably add the binary ops, |
bcf487d to
675d8d1
Compare
|
Okay, I added a ton more trait bounds. At this point unless I missed one, SimdElement has bounds for everything that Only exception is Shl/Shr on int, where I only pass through This does mean that if we ever need to expand the set of supported scalar types, it might be painful because the trait bounds are tighly coupled to exactly what rust core implements on the primitives... But otoh major new scalar types that are also supported by SIMD ISAs feels like a breaking change anyway. The only thing I can think of is maybe I was a bit worried that the HRTBs to allow binary operations against &T would force more bounds in calling code, but my example bitpacking code above still builds happily with all these new bounds, so it seems okay. The expansion also means that the element traits are now pretty usable even without num-traits. All the basic math stuff works fine, the major things num-traits adds is wrapping/saturating arithmetic for ints, and access to a bunch of utility math functions (e.g. log, exp, trig, construct a nan, construct an infinity, ...) for floats. So... yeah, should be a pretty complete set of functionality now 😂 PTAL. |
cb51504 to
78f335c
Compare
|
Also updated the changelog entries and commit msg to describe the new scope of the change, since num-traits is now only necessary if you need access to the additional stuff not exposed by core traits. |
|
Okay, so apparently all you need to do to remove the use fearless_simd::{Simd, SimdElement, SimdInt};
use num_traits::One;
#[inline(always)]
fn unpack_aligned<S: Simd, T: SimdInt<S>>(
simd: S,
mut w: T,
bits_per_element: usize,
reference: T::Element,
out: &mut [T::Element],
) {
let mask = (T::Element::one() << bits_per_element) - T::Element::one();
let count = T::Element::BITS / bits_per_element;
for i in 0..count {
((w & mask) + reference).store_slice(&mut out[i * T::N..(i + 1) * T::N]);
w >>= bits_per_element as u32;
}
}We can probably re-export those from |
|
We did have requests to support |
|
Considering how far we've managed to get without |
19fd92b to
a4003ce
Compare
SimdElement is now bound by many Rust core numeric and utility traits, allowing generic code to work with SimdBase::Element values. Also introduce SimdIntElement and SimdFloatElement subtraits, which add even more numeric and utility operations that only work on ints or floats respectively. Finally, add an optional dependency on num-traits. When the 'num-traits' feature is enabled, SimdElement/SimdIntElement/SimdFloatElement get even more trait bounds for functionality not covered by traits in Rust core. Updates linebender#299 Signed-off-by: David Anderson <dave@natulte.net>
…them possible to chain, add compile test
a4003ce to
6ec54da
Compare
|
Dropping num-traits would be reasonable, I think. Looking at what we lose, the major things I see are Bounded, FromPrimitive/ToPrimitive, FromBytes/ToBytes. zero() and one() can be obtained with Maybe we should add TryFrom and Into/TryInto bounds, as a substitute for FromPrimitive/ToPrimitive? But just It's also easy to add more bounds in function definitions for extra requirements. Supporting core traits makes it much nicer out of the box. The num-traits functionality would be nice, but I don't strictly need it myself so it's a tradeoff between more usability and maintenance burden. I'm happy either way. I rebased the stack to get rid of a merge conflict, and added a patch on top to strip num-traits out again, if you decide you prefer to remove it. |
The core traits provide enough operations out of the box, and generic code that needs more can always add more bounds at the call site. Signed-off-by: David Anderson <dave@natulte.net>
6ec54da to
4442152
Compare
|
Yeah, I kinda want FromPrimitive/ToPrimitive, but I'm not sure depending on num-traits for this is the best path forward. So I think I'll merge this as-is and we can easily add extra bounds later since the trait is sealed. Thank you! |
Also add refined bounds on SimdInt and SimdFloat, allowing access to int-only/float-only operations.
The num-traits dependency is optional, and adjusts available float ops based on fearless_simd's std and libm features.
Updates #299
Implementing this is slightly awkward, because we can't add
#[cfg]conditionals on supertrait bounds. Given that theSimdElementtrait is fairly small, I ended up defining it twice with different bounds.I could have also defined a helper trait instead, but it ends up being close to the same amount of code, and pollutes the docs with a trait that only exists to work around a missing rustc feature. So, I decided that duplicating the definition was the lesser evil.
Additionally, I defined
SimdIntElementandSimdFloatElementtraits, and madeSimdInt::Element/SimdFloat::Elementrequire those bounds as well. This means generic code can choose to be generic over all SIMD types with restricted operations, or generic over only int/only floats, with the full set of ops available.One question: num-traits supports the same no_std-friendly features as fearless_simd, and has almost no dependencies. It also should not break existing code, because the SimdElement trait is sealed and this change doesn't alter which types implement it. Given that, should we enable it by default so that T::Element is more useful out of the box?
Below is a big unrolled list of all the core and num-traits traits that the bounds in this patch add, to give an idea of the feature surface available.
SimdElement provides
SimdIntElement additionally provides
SimdFloatElement additionally provides