Skip to content

Fix handling of zero-size arrays #21

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

Merged
merged 1 commit into from
Oct 27, 2018
Merged

Conversation

jturner314
Copy link
Contributor

Before, if m > 0 && n > 0 && k == 0, the result was never initialized or calculated. Additionally, in some other cases, pointer offsets could result in undefined behavior due to violating the constraints on the .offset() method. (For example, consider the case where m == 0 && n > 0 and the c pointer is a null pointer (which is valid because the c matrix is empty). Then, the call c.stride_offset(csc, knc * l5) was undefined behavior.)

jturner314 added a commit to rust-ndarray/ndarray-stats that referenced this pull request Sep 30, 2018
LukeMathWalker pushed a commit to rust-ndarray/ndarray-stats that referenced this pull request Oct 1, 2018
* Patch matrixmultiply for zero-sized arrays

See bluss/matrixmultiply#21.

* Fix test_covariance_zero_observations

The dot product should be all zeros, and the `dof` is `0. - (-1.) =
1.`, so all elements in the result should be `0. / 1. = 0.`. This
matches the result from NumPy.
@@ -115,6 +115,20 @@ unsafe fn gemm_loop<K>(
where K: GemmKernel
{
debug_assert!(m * n == 0 || (rsc != 0 && csc != 0));
if m == 0 || k == 0 || n == 0 {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for finding this! I think this should be just "if k == 0" ? That's the case where the C matrix can have more than 0 elements. And a comment, // if A or B have no elements, compute C ← βC and return

Benchmarks pass for me with this PR, so it should be fine as it is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the comment.

Yeah, the m == 0 and n == 0 conditions aren't necessary, but I added them for a few reasons:

  1. The rest of the implementation no longer has to worry about m or n being zero.
  2. For people reviewing the code, it's easy to see that this block works correctly for m == 0 and n == 0, while it's less obvious that's true for the rest of the implementation.
  3. For these cases, it's nice to finish early instead of continuing to the rest of the implementation.

I can remove m == 0 and n == 0 if you'd prefer, though.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense like this!

Before, if `m > 0 && n > 0 && k == 0`, the result was never
initialized or calculated. Additionally, in some other cases, pointer
offsets could result in undefined behavior due to violating the
constraints on the `.offset()` method. (For example, consider the case
where `m == 0 && n > 0` and the `c` pointer is a null pointer (which
is valid because the `c` matrix is empty). Then, the call
`c.stride_offset(csc, knc * l5)` was undefined behavior.)
@bluss
Copy link
Owner

bluss commented Oct 27, 2018

@jturner314 is it valid to have C a null pointer just because it is an empty matrix? I'm not sure

@bluss bluss merged commit 80b1f00 into bluss:master Oct 27, 2018
@jturner314
Copy link
Contributor Author

is it valid to have C a null pointer just because it is an empty matrix?

Without the m == 0 condition in this PR, the answer is "no" according to the docs of offset because of this line. However, according to the LLVM docs for getelementptr inbounds (offset is equivalent to getelementptr inbounds), it is safe to offset a null pointer by zero bytes, so in practice, it should be fine.

With the m == 0 and n == 0 constraints in this PR, though, a null pointer is clearly safe because it's never offset, dereferenced, etc.

@bluss
Copy link
Owner

bluss commented Dec 2, 2018

@jturner314 thanks again for this nice fix. I'd like to say that just because the code allows nulls in this state, I'm not sure it's a good idea to "open up" for that possibility, I'd prefer if we required the pointers were non-null.

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