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

Fix for non-contiguous strides #4736

Merged

Conversation

viclafargue
Copy link
Contributor

Fixes #4731

@viclafargue viclafargue requested a review from a team as a code owner May 17, 2022 14:00
@github-actions github-actions bot added the Cython / Python Cython or Python issue label May 17, 2022
@cjnolet cjnolet added this to PR-WIP in v22.08 Release via automation May 23, 2022
@viclafargue viclafargue changed the base branch from branch-22.06 to branch-22.08 May 24, 2022 14:26
@github-actions
Copy link

This PR has been labeled inactive-30d due to no recent activity in the past 30 days. Please close this PR if it is no longer required. Otherwise, please respond with a comment indicating any updates. This PR will be labeled inactive-90d if there is no activity in the next 60 days.

@caryr35 caryr35 added this to PR-WIP in v22.10 Release via automation Aug 4, 2022
@caryr35 caryr35 moved this from PR-WIP to PR-Needs review in v22.10 Release Aug 4, 2022
@caryr35 caryr35 removed this from PR-WIP in v22.08 Release Aug 4, 2022
@github-actions
Copy link

This PR has been labeled inactive-90d due to no recent activity in the past 90 days. Please close this PR if it is no longer required. Otherwise, please respond with a comment indicating any updates.

@caryr35 caryr35 added this to PR-WIP in v22.12 Release via automation Oct 18, 2022
@caryr35 caryr35 moved this from PR-WIP to PR-Needs review in v22.12 Release Oct 18, 2022
@caryr35 caryr35 removed this from PR-Needs review in v22.10 Release Oct 18, 2022
@viclafargue viclafargue changed the base branch from branch-22.08 to branch-22.12 November 17, 2022 17:55
@viclafargue viclafargue added bug Something isn't working non-breaking Non-breaking change labels Nov 18, 2022
@viclafargue
Copy link
Contributor Author

rerun tests

Comment on lines +200 to +206
else:
cupy_data = cp.array(data, copy=True, order='C')
self._ptr = cupy_data.data.ptr
self._owner = cupy_data if cupy_data.flags.owndata \
else data
self.order = 'C'
self.strides = cupy_data.strides
Copy link
Contributor

Choose a reason for hiding this comment

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

Since the newly created array should have a conformant CAI, could we just call the constructor again?

Suggested change
else:
cupy_data = cp.array(data, copy=True, order='C')
self._ptr = cupy_data.data.ptr
self._owner = cupy_data if cupy_data.flags.owndata \
else data
self.order = 'C'
self.strides = cupy_data.strides
else:
cupy_data = cp.array(data, copy=True, order='C')
super().__init__(data=cupy_data)

Comment on lines 265 to 267
itemsize = cp.dtype(dtype).itemsize
shape = list(shape)
strides = list(strides)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think there is a need for these copies and you can also just use the sliced view of those arrays directly.

shape = shape[::-1]
for dim_size in shape[:-1]:
strides.append(dim_size * strides[-1])
strides = strides[::-1]

else:
raise ValueError('Order must be "F" or "C". ')
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
raise ValueError('Order must be "F" or "C". ')
raise ValueError('Order must be "F" or "C".')

shape = shape[::-1]
for dim_size in shape[:-1]:
strides.append(dim_size * strides[-1])
strides = strides[::-1]
Copy link
Contributor

@csadorf csadorf Nov 23, 2022

Choose a reason for hiding this comment

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

We could consider to use a combination of itertools.accumulate() and operator.mul() to compute the strides a bit more succinctly:

from itertools import accumulate
from operator import mul


f_strides = list(accumulate(shape[:-1], func=mul, initial=item_size))
c_strides = list(accumulate(shape[:0:-1], func=mul, initial=item_size))[::-1]

Edit: If you like the suggestion, I can run some benchmarks to ensure that this isn't slower by any chance.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the review. Sure that could be interesting :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like loops are the fastest, however I was able to apply a micro optimization that is a bit faster (see below):

Loops:              100.00%
Loops (alt):        94.73%
With accumulate:    132.87%

I ran this a few times and results appear pretty consistent.

Benchmark code
from itertools import accumulate
from operator import mul


def compute_strides(shape, item_size):
    tuple(accumulate(shape[:-1], func=mul, initial=item_size))
    tuple(accumulate(shape[:0:-1], func=mul, initial=item_size))[::-1]


def compute_strides_loops(shape, item_size):

    strides = [item_size]
    for dim_size in shape[:-1]:
        strides.append(dim_size * strides[-1])
    tuple(strides)

    strides = [item_size]
    shape = shape[::-1]
    for dim_size in shape[:-1]:
        strides.append(dim_size * strides[-1])
    tuple(strides[::-1])


def compute_strides_loops_alternative(shape, item_size):

    strides = [item_size]
    for dim_size in shape[:-1]:
        strides.append(dim_size * strides[-1])
    tuple(strides)

    strides = [item_size]
    for dim_size in shape[:0:-1]:
        strides.append(dim_size * strides[-1])
    tuple(strides[::-1])


if __name__ == "__main__":
    from timeit import timeit

    result_w_loops = timeit(
        "compute_strides((2, 3), 8)",
        setup="from benchmark_strides import compute_strides_loops as compute_strides",
    )

    result_w_loops_alt = timeit(
        "compute_strides((2, 3), 8)",
        setup="from benchmark_strides import compute_strides_loops_alternative as compute_strides",
    )

    result_w_accumulate = timeit(
        "compute_strides((2, 3), 8)",
        setup="from benchmark_strides import compute_strides",
    )

    print(f"Loops:              {result_w_loops / result_w_loops:.2%}")
    print(f"Loops (alt):        {result_w_loops_alt / result_w_loops:.2%}")
    print(f"With accumulate:    {result_w_accumulate / result_w_loops:.2%}")

Micro-optimization:

    # F-order
    strides = [item_size]
    for dim_size in shape[:-1]:
        strides.append(dim_size * strides[-1])
    tuple(strides)

   # C-order
    strides = [item_size]
    for dim_size in shape[:0:-1]:
        strides.append(dim_size * strides[-1])
    tuple(strides[::-1])

v22.12 Release automation moved this from PR-Needs review to PR-Reviewer approved Nov 29, 2022
@dantegd
Copy link
Member

dantegd commented Nov 29, 2022

@gpucibot merge

@rapids-bot rapids-bot bot merged commit 07f0bc4 into rapidsai:branch-22.12 Nov 29, 2022
v22.12 Release automation moved this from PR-Reviewer approved to Done Nov 29, 2022
jakirkham pushed a commit to jakirkham/cuml that referenced this pull request Feb 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working Cython / Python Cython or Python issue inactive-30d inactive-90d non-breaking Non-breaking change
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

[BUG] Non-standard input data striding isn't supported by all estimators
3 participants