Skip to content
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

Merged
merged 2 commits into from
Sep 30, 2019

Conversation

bluss
Copy link
Member

@bluss bluss commented Sep 29, 2019

  1. Add ArrayView/Mut::new constructor from NonNull pointers (for internal use)

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.

  1. Add .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, 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.

src/impl_raw_views.rs Outdated Show resolved Hide resolved
src/impl_raw_views.rs Outdated Show resolved Hide resolved
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.
@bluss bluss merged commit ecb7643 into master Sep 30, 2019
@bluss bluss deleted the raw-view-cast branch September 30, 2019 19:42
@bluss
Copy link
Member Author

bluss commented Oct 1, 2019

@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.

Copy link
Member

@jturner314 jturner314 left a 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,
Copy link
Member

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).

Copy link
Member Author

@bluss bluss Oct 2, 2019

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.

Copy link
Member

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.

Copy link
Member

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,
Copy link
Member

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`
Copy link
Member

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);
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants