-
Notifications
You must be signed in to change notification settings - Fork 83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use specialized traits to generalize lane comparisons #206
Conversation
If they should be public so people can use them generically then why should the methods never be stabilized? |
The traits are used in the bounds of the member fns of |
what is the harm if someone did? |
There's no harm, it just complicates the API. There's no good reason to expose it because then we have the liberty to change it in the future. I suggested hiding them to simplify the docs as well. The trait is effectively a marker to map type to intrinsic. A similar trait is |
It's worth noting that |
I just (1) am fundamentally against perma-unstable anything (2) the kind of person that frequently implements traits for newtypes via macro, and i would usually like the possibility to implement all of a base type's traits on the newtype as well, should it be appropriate. |
In this case, these traits are just mapping intrinsics to types. They're sealed so users can't implement them on newtypes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, though the Simd
methods could just call the intrinsics directly if we wanted, rather than calling the methods on SimdOrd
which then calls the intrinsics.
so, kinda like: pub trait SimdOrd: ... {}
impl SimdOrd for f32 {}
impl<T: SimdOrd, const LANES: usize> Simd<T, LANES> {
pub fn max(self, rhs: Self) -> Self {
unsafe { max_intrinsic(self, rhs) }
}
} |
That isn't possible in some cases, since the implementations for different types aren't the same (for example, |
I expect we will definitely want fp min/max intrinsics (llvm may have issues recognizing the patterns)...so, while we're at it, just have int min/max intrinsics too. for this pr, yeah, we can just leave it with the calling |
NonZero types that interact with their "base" type using ops that return a type other than bool have ops that return the NonZero type, not the underlying type. |
All I'm saying is that |
If there's no harm in having them exposed, then we should simply have them exposed, unless we actually intend to remove them later. And if this proposal is serious, then we do not. I disagree that it simplifies the docs: This is true even if the trait is also sealed, and there is no reason to leave these traits as permanently unstable. People should be able to depend on the trait after stabilization for the types for which it is implemented for. |
I understand your sentiment but I disagree with what these traits mean. Identically, something that's already in |
There is a tracking issue for that trait's stabilization. So no, I do not believe that reasoning applies. |
I would argue the other way: |
I guess whether or not it's part of the API is somewhat immaterial to this PR--I wish it weren't but it doesn't make a huge difference either way. Like |
Regarding SimdAbs: I suggested traits because I felt we should have {Simd?}Float and {Simd?}{S,U,}Int, not SimdAbs, and that the traits should define that the vectors they are implemented for implement a natural subset of the behaviors of floats or integers, with very well-defined variations. That would keep the number of traits down to a sane and simple scope. I did offer a SimdAbs impl variation, briefly, but I no longer am inclined to such. Even so, I am not perturbed by the possible asymmetry. |
This is not a good direction to go in, IMO. As far as I can tell, the only reason to have Keep in mind: even having People who want Similarly, TBH, given the bikeshedding and discussion about hypothetical future use cases we won't be able to add compatibly... I can see why the stdlib usually opts not to make traits for these things (or opts not to make them public). Designing a trait like this without excluding some hypothetical future use cases is both impossible, and seen as the Worst Possible Thing. |
Okay, I'm going to roll the discussions from the comment thread and the main thread into this comment, to go through all the critiques in one place. Are these traits required at all? User types Library newtypes Implementing newtypes by extending intrinsics Imagine you want to add support for an arbitrary newtype (let's use all of the
My concerns with this approach:
While this approach could theoretically work, I think it makes more sense for the intrinsics to only use primitive types, and handle logic around newtypes within |
I do not desire this, I stated that it is what limits the current interface, and that we originally designed SimdElement to mean "A machine scalar type", exactly matching the bounds in typeck as we found them. I do not desire this proposed extension, and I do not desire this infinite increase to the scope of our work. I do not want you to say I "suggested" something when I enumerate alternate paths, as it would have to mean I desire it, or even think it is a good idea, for it to actually be a suggestion. I have stated this boundary multiple times. Please do not transgress it again. I agree with Thom. We should not go down this path. |
Sorry, when I said suggest I didn't mean "advocate", I meant "propose to be considered". You're right that newtypes could potentially be handled by wrapping Maybe it was my mistake picking the comparison functions first, but I wanted to target the most complicated ones first. If we don't go down this path, can I ask how we should implement |
Thank you. I prefer "enumerate", as it is appropriately tediously dry and void of implication beyond the monotonic iteration of a thinking machine over multiple variations. |
After a discussion with @workingjubilee, I've made some changes that I believe clarify the purpose and reasoning behind these changes. There are two traits:
These traits indicate vector semantics, marking which vector operations can be done. The implementations of these operations are handled by sealed traits. The reasoning behind these changes is simply that different types need different implementations, and relying on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
generally lgtm, though we should consider renaming lanes_eq
to eq
(and other similar functions) or max
to lanes_max
(and other similar functions) so the lane-wise operations are named consistently.
A simpler variant of #206. * Comparisons are moved to `SimdPartialEq`, `SimdPartialOrd`, and `SimdOrd`. The function names are prefixed with `simd_` to disambiguate from the regular `PartialEq` etc functions. With the functions on traits instead of `Simd` directly, shadowing the function names doesn't work very well. * Floating point `Ord`-like functions are put into a `SimdFloat` trait. The intention is that eventually (some time after this PR) all floating point functions will be moved from `Simd` to `SimdFloat`, and the same goes for future `SimdInt`/`SimdUint` traits.
This fixes the issue where lane comparisons are determined by
PartialEq
andPartialOrd
, which isn't correct. It also allowsmin
,max
,clamp
, andhorizontal_{min,max}
to be generic over element type. This means that this PR also addsmin
,max
, andclamp
to integers.The new traits
SimdEq
andSimdOrd
probably should be part of the public interface, to allow calling the functions generically, but the functions in those traits should never be stabilized (and should probably be hidden, once that bug is worked out). Both of these traits are sealed bySimdElement
.We can use the results of this PR to inform the conversion of other functions to generic over element type.