-
Notifications
You must be signed in to change notification settings - Fork 307
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
Simplify ArrayView construction from NonNull<T> and add RawView .cast() method #734
Conversation
This saves some unwrapping/rewrapping in NonNull, by simpliy copying `self.ptr` along. For the array views, the previous new_ constructor still remains for use in all places we make array views directly from raw pointers.
Add methods for raw view casts, this makes it easier to build upon the raw array view functionality. Of course, adding more capabilities for the unsafe APIs opens for users to make mistakes, but we need these capabilities ourselves for developing features for ndarray. The reason for this to exist is for the `ArrayViewMut<T>` → `ArrayView<Cell<T>>` transformation demonstrated in the tests. However, we don't add a method for this directly, at least not yet. The reason is that we'd like to have a type that is exactly like `Cell<T>` but also supports the arithmetic ops.
45941cf
to
ac55d74
Compare
@jturner314 I'd like your review on this, because there might be facets of this I have overlooked, that mean this should be an unsafe function. We have the size assert as a firm check, since it's a check that compiles out when it passes, that's simple to do. |
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.
I've reviewed the PR. The only significant issue is that the alignment checks in cast
need to be assert!
s instead of debug_assert!
s, since ArrayBase
requires that its ptr
be aligned. I added a suggestion of how to avoid the performance cost of the check in most cases.
The simplification of the constructor calls is really nice.
is_aligned(ptr.as_ptr()), | ||
"alignment mismatch in raw view cast" | ||
); | ||
/* Alignment checked with debug assertion: alignment could be dynamically correct, |
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.
Since this is a safe function, we need to either assert!
(not debug_assert!
) that the pointer is aligned (which would be my preference), or we need to relax the "ptr
must always be aligned" requirement on ArrayBase
.
To avoid the cost of the check in most cases, we could write it like this:
if mem::align_of<A>() % mem::align_of<B>() != 0 {
assert!(is_aligned(ptr.as_ptr()), "incompatible alignment in raw view cast");
}
or, equivalently, since "alignment is measured in bytes, and must be at least 1, and always a power of 2":
if mem::align_of<B>() > mem::align_of<A>() {
assert!(is_aligned(ptr.as_ptr()), "incompatible alignment in raw view cast");
}
The comparison in the if
should always compile out, and the body of the if
will remain only if the alignment of B
is greater than the alignment of A
. I would expect that case to be relatively rare, because it requires additional work on the part of the user (since the pointer to A
won't naturally be guaranteed to have valid alignment for B
).
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.
Where does the alignment requirement come from, and doesn't it only matter if we dereference?
I'd prefer to make this function unsafe, there is basically no downside to that, instead of adding checking that does not compile out.
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.
I'm referring to this constraint on ArrayBase
. From what I can tell, you're right -- the only time alignment actually matters for RawArrayView/Mut
is when dereferencing a pointer or reading/writing a pointed-to value in some way (i.e. most of the functions in std::ptr
).
It sounds like the best option is to remove the alignment restriction on RawArrayView/Mut
. All we'd need to do is update the list of restrictions in that comment above ArrayBase
, the docs for RawArrayView/Mut::from_shape_ptr
, and the docs for deref_into_view/_mut
.
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.
I've created #738 to remove the alignment restriction on RawArrayView/Mut
.
is_aligned(ptr.as_ptr()), | ||
"alignment mismatch in raw view cast" | ||
); | ||
/* Alignment checked with debug assertion: alignment could be dynamically correct, |
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.
Same thing here -- the alignment check is a safety issue, so it should be assert!
, not debug_assert!
.
dim, | ||
strides, | ||
} | ||
} | ||
|
||
/// Create a new `ArrayView` |
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.
Typo: This should be ArrayViewMut
(same for the docs of new
above).
let a = Array::<(), _>::default((250, 250)); | ||
let b: RawArrayView<Zst, _> = a.raw_view().cast::<Zst>(); | ||
assert_eq!(a.shape(), b.shape()); | ||
assert_eq!(a.as_ptr() as *const u8, b.as_ptr() as *const u8); |
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.
I'd write this as
assert_eq!(a.as_ptr().cast::<()>(), b.as_ptr().cast::<()>());
or
assert_eq!(a.as_ptr() as usize, b.as_ptr() as usize);
but that's just personal preference.
This saves some unwrapping/rewrapping in NonNull, by simpliy copying
self.ptr
along.For the array views, the previous new_ constructor still remains for use in
all places we make array views directly from raw pointers.
.cast::<B>()
method on raw views. Matches NonNull::cast.Add methods for raw view casts, this makes it easier to build upon the
raw array view functionality.
Of course, adding more capabilities for the unsafe APIs opens for users
to make mistakes, but we need these capabilities ourselves for
developing features for ndarray.
The reason for this to exist is for the
ArrayViewMut<T>
→ArrayView<Cell<T>>
transformation demonstrated in the tests. However, wedon't add a method for this directly, at least not yet. The reason is
that we'd like to have a type that is exactly like
Cell<T>
but alsosupports the arithmetic ops.