Skip to content

Fast access #500

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 2 commits into from
Nov 16, 2016
Merged

Fast access #500

merged 2 commits into from
Nov 16, 2016

Conversation

SylvainCorlay
Copy link
Member

@SylvainCorlay SylvainCorlay commented Nov 15, 2016

A first take at the recursive version of access operators + moving the dimension checks up the call stack.

Trying to respect your code formatting conventions.

Closes #497 .

@dean0x7d
Copy link
Member

On the topic of micro-optimizing the offset function, implementation 2 in the following code generates ideal assembly: https://godbolt.org/g/o2HC2G -- you might want to consider it.

@SylvainCorlay
Copy link
Member Author

This actually looks really good.

@JohanMabille
Copy link
Contributor

Indeed, we should use it in xtensor.

fail_dim_check(sizeof...(index), "too many indices for an array");
return get_byte_offset(index...);
template<typename... Ix> size_t offset_at(Ix... index) const {
check_dimensions(index...);
Copy link
Member

@aldanor aldanor Nov 15, 2016

Choose a reason for hiding this comment

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

This seems wrong? Note that the check here is for > whereas in other places it is for !=, otherwise it would have been implemented like you suggest here via a check_dimensions(...) helper.

}

size_t get_byte_offset() const { return 0; }
Copy link
Member

Choose a reason for hiding this comment

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

I guess I'd still prefer to have a separate byte_offset_unchecked() which would be used by all other methods (and xtensor), and having all other methods (including byte_offset()) be checked, so it's 'safe by default'.

@SylvainCorlay SylvainCorlay force-pushed the fast-access branch 2 times, most recently from 0dfff62 to 5363c56 Compare November 15, 2016 16:08
@SylvainCorlay
Copy link
Member Author

@aldanor @dean0x7d this version should be functionally equivalent and includes the fast proposal of Dean.

@SylvainCorlay
Copy link
Member Author

So now byte_offset remains unsafe, but all calls to it in pybind call a check_dimensions first.

@aldanor
Copy link
Member

aldanor commented Nov 15, 2016

Looks good.

#500 (comment) still stands though :)

How about this?

template<size_t dim = 0, typename... Ix> size_t byte_offset_unchecked(size_t i, Ix... index) const
{ /* current impl, checks nothing */ }

template<typename... Ix> size_t byte_offset(Ix... index) const {
    if (sizeof...(index) > ndim())
        fail_dim_check(sizeof...(index), "too many indices for an array");
    check_dimensions(index...);
    return byte_offset_unchecked(index...);
}

@SylvainCorlay
Copy link
Member Author

ok, either that or byte_offset_safe?

@aldanor
Copy link
Member

aldanor commented Nov 15, 2016

(the reasoning being: get_byte_offset() was previously part of private API; now byte_offset() is public so we make it safe by default)

@aldanor
Copy link
Member

aldanor commented Nov 15, 2016

Ideally, I see unsafe version as one that's longer to type and that has a scary suffix like "unsafe" / "unchecked" / w/e, which also conveys the fact that it's "non-default". Since the difference is that it skips bounds checking, "_unchecked" makes sense, imo.

@SylvainCorlay
Copy link
Member Author

SylvainCorlay commented Nov 15, 2016

I agree that any function callable directly from python should be safe and not crash the interpreter for any parameter value, but in the present case, accessors are only to be used from c++.

It is usual for c++ container to be unchecked (like std::vector::operator[])...

@aldanor
Copy link
Member

aldanor commented Nov 15, 2016

Agreed. But we're trying to mirror ndarray behaviour here (to some extent, anyway...), hence the divergence.

@dean0x7d
Copy link
Member

dean0x7d commented Nov 15, 2016

Least surprise: If most functions are checked by default, it's natural to assume they all are. Making just one silently unsafe will cause issues for users.

@SylvainCorlay
Copy link
Member Author

SylvainCorlay commented Nov 15, 2016

I disagree with you guys, but I made the change here to get this part through.

I will open another issue for discussion on this point and something else that came up.

template<size_t dim = 0, typename... Ix> size_t byte_offset(size_t i, Ix... index) const {
return i * strides()[dim] + byte_offset<dim + 1>(index...);
template<typename... Ix> size_t byte_offset(Ix... index) const {
check_dimensions(index...);
Copy link
Member

Choose a reason for hiding this comment

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

Dimensionality check, too? (<)

Copy link
Member Author

Choose a reason for hiding this comment

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

There was none originally.

Copy link
Member

Choose a reason for hiding this comment

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

Shall we add one then?

@aldanor
Copy link
Member

aldanor commented Nov 15, 2016

Btw: it's all quite trivial, but adding a test for byte_offset would be nice since it's public now.

@SylvainCorlay
Copy link
Member Author

SylvainCorlay commented Nov 15, 2016

Btw, it is protected, not public.

@aldanor
Copy link
Member

aldanor commented Nov 15, 2016

Ah, true, apologies.

All in all lgtm I think, great bikeshedding :)

@dean0x7d
Copy link
Member

Sorry, I also missed the protected bit.

@aldanor
Copy link
Member

aldanor commented Nov 15, 2016

Yea, viewing diffs on iPhone is hard, sorry :) To reiterate, the public API we really want to be safe by default, if it's protected then it could just stay unchecked of course. Or, you could have both like now and make the unsafe one public as well, whichever makes most sense.

@wjakob
Copy link
Member

wjakob commented Nov 15, 2016

Btw: it's all quite trivial, but adding a test for byte_offset would be nice since it's public now.

👍

@SylvainCorlay
Copy link
Member Author

@wjakob we cannot test this function externally because it is not public.

Only thing we could do in the test is inherit from it and expose a public function that calls it.

However this is already pretty much what offset_at` does. And it is already tested.

@wjakob
Copy link
Member

wjakob commented Nov 15, 2016

Got it. Does it make sense to add a dimensionality test? (see the code review comments)

@SylvainCorlay
Copy link
Member Author

SylvainCorlay commented Nov 15, 2016

There was not such check before. The current state of the PR is functionally equivalent to the previous one, but should be faster.

@SylvainCorlay
Copy link
Member Author

Well my code is functionnally 5

On Nov 15, 2016 10:47 AM, "Wenzel Jakob" [email protected] wrote:

Got it. Does it make sense to add a dimensionality test (see the code
review comments)


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
#500 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/ACSXFn8JPa8CeStX8Xyx3Gf1tA1LREMLks5q-f4EgaJpZM4KyoPS
.

@wjakob
Copy link
Member

wjakob commented Nov 15, 2016

Yes, but since you are already changing that code and moving tests around, it would be nice to fix this omission :)

@SylvainCorlay
Copy link
Member Author

SylvainCorlay commented Nov 15, 2016

Also there are already checks for != and < (depending on the case) in the
calling code.

That would be duplicating the checks.

@SylvainCorlay
Copy link
Member Author

Just realized this is not merged yet. Is there any blocker?

@wjakob
Copy link
Member

wjakob commented Nov 16, 2016

What I don't understand is why the dimensionality check in offset_at (et al.) is not moved into check_dimensions?

@SylvainCorlay
Copy link
Member Author

SylvainCorlay commented Nov 16, 2016

Because they differ depending of the function. Some check for dimension inequality and other for dimension mismatch. This was one of the items of the review by aldanor.

@wjakob
Copy link
Member

wjakob commented Nov 16, 2016

Ok, I think I get it now -- thanks!

@wjakob wjakob merged commit 5027c4f into pybind:master Nov 16, 2016
@SylvainCorlay SylvainCorlay deleted the fast-access branch November 16, 2016 17:00
@SylvainCorlay
Copy link
Member Author

Cool thanks!

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.

5 participants