-
Notifications
You must be signed in to change notification settings - Fork 58
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
Updating get_row functions to return MatrixSlice #99
Conversation
A reasonable vectorization test would be: // With `iter_rows_mut` returning `&mut [T]`
for row in mat.iter_rows_mut() {
for v in row {
*v += 2;
}
}
// Versus
for row in mat.iter_rows_mut() {
row += 2;
} |
This PR is now ready for some initial reviews: @Andlon @andrewcsmith if either of you have time I would appreciate it! I will update the top post above with more information soon. The gist is that I think I've settled on the API, but we still need some more quality of life changes. Some notable ones:
Those are the biggest right now (I think). I'll try to write a complete list up later. |
@@ -1,12 +1,11 @@ | |||
use std::iter::{ExactSizeIterator, FromIterator}; | |||
use std::mem; | |||
use std::slice; | |||
//use std::slice; |
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.
Before final merge: Remove this?
@@ -89,7 +86,10 @@ impl<'a, T> Iterator for $rows<'a, T> { | |||
unsafe { | |||
// Get pointer and create a slice from raw parts | |||
let ptr = self.slice_start.offset(self.row_pos as isize * self.row_stride); | |||
row = slice::$slice_from_parts(ptr, self.slice_cols); | |||
//row = slice::$slice_from_parts(ptr, self.slice_cols); |
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.
Before final merge: remove this?
@@ -117,7 +120,10 @@ impl<'a, T> Iterator for $rows<'a, T> { | |||
let row: $row_type; | |||
unsafe { | |||
let ptr = self.slice_start.offset((self.row_pos + n) as isize * self.row_stride); | |||
row = slice::$slice_from_parts(ptr, self.slice_cols); | |||
//row = slice::$slice_from_parts(ptr, self.slice_cols); |
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.
Before final merge: remove this?
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 left these here for easier benchmarking later. Just so I can quickly check if something is funky with this iterator.
Edit: I mean to say that I will indeed remove them, thank you for flagging.
* Implement IntoIterator for MatrixSlice * Add simple compile test for IntoIterator * Replace new lifetime with explicit lifetime for return value * Implement IntoIterator for MatrixSliceMut * Move IntoIterator implementation to iter.rs Since it is an iterator trait it fits better there * Test that mutable iterators are mutable
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.
Even though you are not finished, I think it looks very good already! Will be a very welcome change to the API :)
I've looked really hard, but I can't find any changes to fn row(index)
on BaseMatrix
anywhere, yet you seem to have changed this, according to the examples... Am I blind here?
In any case, I get the impression from the examples that you still return an Option
from the row
/row_mut
etc. functions. What's your thoughts on this...?
impl_iter_rows!(Rows, &'a [T], from_raw_parts); | ||
impl_iter_rows!(RowsMut, &'a mut [T], from_raw_parts_mut); | ||
// impl_iter_rows!(Rows, &'a [T], from_raw_parts); | ||
// impl_iter_rows!(RowsMut, &'a mut [T], from_raw_parts_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.
Before final merge: remove this?
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.
Thanks, will remove this soon!
let (lower_rows, _) = iterator.size_hint(); | ||
cols = row.row.cols(); | ||
|
||
mat_data = Vec::with_capacity(lower_rows.saturating_add(1).saturating_mul(cols)); |
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.
Why saturating arithmetic here? I'm a bit confused about this part.
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.
That is a good question. I'm not sure what my initial motivation was but there is no real reason.
} | ||
|
||
for row in iterator { | ||
assert!(row.row.cols() == cols, "Iterator slice length must be constant."); |
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.
The message could perhaps be a bit clearer for the user. For example, "Iterator of rows must have rows of compatible dimensions." or so?
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.
Yes you're right. I overlooked this when I copied the existing FromIterator
implementation. Also - do you think we should remove the FromIterator<&'a [T]>
implementation? I think we probably should.
@@ -105,7 +105,10 @@ impl<'a, T> Iterator for $rows<'a, T> { | |||
unsafe { | |||
// Get pointer to last row and create a slice from raw parts | |||
let ptr = self.slice_start.offset((self.slice_rows - 1) as isize * self.row_stride); | |||
Some(slice::$slice_from_parts(ptr, self.slice_cols)) | |||
//Some(slice::$slice_from_parts(ptr, self.slice_cols)) |
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.
Before final merge: Remove this?
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.
Just noting that I kept this here to make it a little easier to benchmark later. Will remove later.
/// use rulinalg::matrix::BaseMatrixMut; | ||
/// | ||
/// let mut mat = matrix![1.0, 2.0; | ||
/// 3.0, 4.0]; |
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.
Minor detail: is the alignment off also in the rendered docs?
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.
Thanks for pointing this out. It probably is.
/// 3.0, 4.0]; | ||
/// | ||
/// { | ||
/// let mut row = mat.row_mut(1).unwrap(); |
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.
Can we remove the Option
alltogether and just panic when index is out of bounds instead?
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.
Yes! I had forgotten that we discussed this before and I think this would be better. I can't imagine a case where a user would benefit from having an Option
.
I think I answered all your questions except for:
This is because this function remained unchanged (except for the name and return type). The change was in |
I just realized that GitHub hadn't shown me all the changes! I had to press "Load Diff" at the bottom of the screen. Never experienced that before... Will take a look at the rest too |
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.
OK, I looked at the rest too. Not much more to add to my previous review :)
/// # fn main() { | ||
/// use rulinalg::matrix::{Matrix, BaseMatrix}; | ||
/// | ||
/// let mat = Matrix::new(3,3, (0..9).collect::<Vec<usize>>()); |
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 know this is practical, but I personally find it easier to immediately understand examples if the example matrices are actually typed out when they are so small. It leaves the user to focus on the logic that's being showcased rather than having to parse the matrix construction logic. Not a big deal though!
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.
No I agree, I'll make this change too.
} else { | ||
None | ||
} | ||
} | ||
|
||
/// Returns a mutable reference to the row of a matrix at the given index | ||
/// without doing unbounds checking | ||
/// without doing a bounds check. |
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.
Haha, glad you got this one too. Let's remember to close #85 when you're done here ;)
Benchcmp output:
The only non-noisy changes are SVD and diag iterator which should be unchanged with this. I'm not sure why they were so significantly affected but I'm not too concerned. |
Looks great to me :) I've noticed that the output of benchmarks can change significantly unless I completely shut down all other programs running in the background (despite running on a quadcore i7 with low load). Guess it's due to the Linux scheduler and/or memory access patterns. Might account for the noise in the SVD benchmarks. |
I see similar things myself which is why I wasn't too concerned. I'll do some vectorization tests and if those look good then hopefully one more look through should make this merge-ready! |
I did a little digging into vectorization - for the most part it looks good to me. All of the operations break down into per-row operations. We may incur some cost for creating the row iterator unnecessarily (instead of the user iterating through the slice) but I don't think it will be significant. Otherwise I can't find any cases where treating the row as a matrix instead of a |
Ok I think I'm happy with this PR. Here are my thoughts on some outstanding things:
I'd love to hear from @Andlon , @andrewcsmith and anyone else to see if they spot anything else. |
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.
Looks good to me! (just slightly confused by a TODO that didn't make sense to me, assume it is out-of-date?)
@@ -326,7 +326,8 @@ mod tests { | |||
assert!(!row.iter().take(idx).any(|&x| x > 1e-10)); | |||
assert!(!row.iter().skip(idx + 1).any(|&x| x > 1e-10)); | |||
// Assert non-negativity of diagonal elements | |||
assert!(row.raw_slice()[idx] >= 0.0); | |||
// TODO: Fix when indexing fixed | |||
assert!(row[idx] >= 0.0); |
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 assume the TODO is out of date, and that this is actually fixed now?
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.
Ah yes! Forgot to remove it.. thanks
Good idea! I've also had some unpleasant run-ins with those modules.
Makes sense.
Agree with your assessment. There are more important things (and possibly more interesting?) for us to spend time on, I think :) |
I'm going to give this 12 hours in case @andrewcsmith wants to take a look then I'll be looking to merge. As there are breaking changes here I'm going to try to pick out the issues we should get in as part of 0.4 release. Ideally we wont have to wait too long before our next release. |
Merging now - mostly to force myself to finish up the other 0.4 issues! |
This is a very incomplete PR. I'm just putting in the request so I can track outstanding work more easily. Still to do:
get_
prefix. (Does this make the unchecked functions look weird?)Row
andCol
.There is some discussion in issue #87 . There are other ideas here which I would like to explore if I have time.
With this PR I'll try to work towards an agreeable API for these changes.
Also resolved #85 with this PR.