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

Support negative preceding/following for ROW window functions #14093

Merged
merged 18 commits into from
Sep 21, 2023

Conversation

mythrocks
Copy link
Contributor

@mythrocks mythrocks commented Sep 12, 2023

Description

This commit adds support for "offset" ROW windows, where the preceding and following
window bounds are allowed to have negative values. This allows window definitions to
exclude the current row entirely.

Prior to this change, ROW-based windows had to include the current row, causing
preceding and following to support only non-negative values. Additionally, the
inclusion of the current row would count against the min_periods check.

The following is an example of the new "negative" semantics. Consider the input:

auto const row = ints_column{1, 2, 3, 4};

If the window bounds are specified as (preceding=3, following=-1), then the window
for the third row (3) is {1, 2}.
following=-1 indicates a "following" row before the current row.

A negative value for preceding follows the existing convention of including the
current row. This makes it slightly more involved:

  1. preceding=2 indicates one row before the current row.
  2. preceding=1 indicates the current row.
  3. preceding=0 indicates one row past (i.e. after) the current row.
  4. preceding=-1 indicates two rows after the current row.
    Et cetera.

min_periods checks continue to be honoured as before, but the requirement for
positive min_periods is dropped. min_periods only need be non-negative.

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

This commit adds support for "offset" ROW windows, where the preceding and following
window bounds are allowed to have negative values.  This allows window definitions to
exclude the current row entirely.

Prior to this change, ROW-based windows *had* to include the current row, causing
`preceding` and `following` to support only non-negative values.  Additionally, the
inclusion of the current row would count against the `min_periods` check.

The following is an example of the new "negative" semantics.  Consider the input:
```c++
auto const row = ints_column{1, 2, 3, 4};
```
If the window bounds are specified as (preceding=3, following=-1), then the window
for the third row (`3`) is `{1, 2}`.
`following=-1` indicates a "following" row *before* the current row.

A negative value for `preceding` follows the existing convention of including the
current row.  This makes it slightly more involved:
  1. `preceding=2` indicates *one* row before the current row.
  2. `preceding=1` indicates the current row.
  3. `preceding=0` indicates one row past (i.e. after) the current row.
  4. `preceding=-1` indicates two rows after the current row.
Et cetera.

`min_periods` checks continue to be honoured as before, but the requirement for
positive `min_periods` is dropped.  `min_periods` only need be non-negative.

Signed-off-by: MithunR <mythrocks@gmail.com>
@mythrocks mythrocks self-assigned this Sep 12, 2023
@mythrocks mythrocks requested a review from a team as a code owner September 12, 2023 17:31
@copy-pr-bot
Copy link

copy-pr-bot bot commented Sep 12, 2023

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@mythrocks mythrocks marked this pull request as draft September 12, 2023 17:31
@github-actions github-actions bot added libcudf Affects libcudf (C++/CUDA) code. CMake CMake build issue labels Sep 12, 2023
@mythrocks mythrocks added feature request New feature or request Spark Functionality that helps Spark RAPIDS non-breaking Non-breaking change labels Sep 12, 2023
@mythrocks
Copy link
Contributor Author

/ok to test

@mythrocks
Copy link
Contributor Author

mythrocks commented Sep 18, 2023

Moved this back to draft. There are a couple of things pending:
1. A Spark test had revealed a possible bug at the group boundaries. That test needs to be formalized in the CUDF test.
2. One if-check can be moved out of the kernel for fixed window bounds.
3. Must merge latest from origin/branch-23.10.

I'll update these very shortly.

Update: This PR is ready for review.

@mythrocks mythrocks marked this pull request as ready for review September 18, 2023 20:58
@mythrocks
Copy link
Contributor Author

A note to the reviewer:

  1. Most of the bulk seems to be the tests.
  2. In grouped_rolling.cu: The new row_based_preceding_calc and row_based_following_calc are a result of refactoring what used to be a simpler lambda. This refactor allows the if (following < 0) check to be constexpr.
  3. Here, an additional change was made to materialize the preceding_column and following_column, so that the rolling kernels need not be instantiated with complicated iterators.

Copy link
Contributor

@robertmaynard robertmaynard left a comment

Choose a reason for hiding this comment

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

We need to update https://github.com/rapidsai/cudf/blob/branch-23.10/cpp/include/cudf/rolling.hpp#L181 to document the relaxation of preceding_window and following_window

cpp/tests/rolling/offset_row_window_test.cpp Show resolved Hide resolved
cpp/tests/rolling/offset_row_window_test.cpp Show resolved Hide resolved
@@ -94,6 +93,109 @@ std::unique_ptr<column> grouped_rolling_window(table_view const& group_keys,

namespace detail {

/// Preceding window calculation functor.
template <bool preceding_less_than_1>
Copy link
Member

Choose a reason for hiding this comment

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

What is the advantage and disadvantage of having this template?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, right. This is to enable the if constexpr() in operator():

    if constexpr (preceding_less_than_1) {  // where 1 indicates only the current row.
      auto group_end = _group_offsets_begin[group_label + 1];
      return thrust::maximum{}(_preceding_window, -(group_end - 1 - idx));
    } else { ... }

When this used to be a lambda, this used to be:

    if (preceding < 1) { ... } else { ... }

That if check would have run once per row in the window. Or worst case N**2 times, over the column. The template was so that this is checked once.

Copy link
Member

Choose a reason for hiding this comment

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

Has there been a tangible benefit to runtime perf with this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry if the refactor isn't immediately obvious. This commit shows the refactor: 589f2a5

Copy link
Contributor Author

@mythrocks mythrocks Sep 20, 2023

Choose a reason for hiding this comment

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

Ah, I don't have a benchmark in place for this.

I did assume that lifting this if check out would be beneficial.

(Sorry for the delayed response. GitHub reply notifications seem to be delayed. )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would you rather I postponed this refactor until I have confirmed the advantage in a benchmark?

I'd be happy to do that if it helps get the main feature in.

Copy link
Member

Choose a reason for hiding this comment

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

No, it's totally fine to get this in as-is as long as there's no significant change to compile times.

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 accommodating, @divyegala.

I've measured how much longer grouped_rolling.cu takes to compile, with all the changes in this PR thrown in.
Old: 98s
New: 93s

It's taking less time to compile now. This might have to do with my materializing the preceding/following columns, to simplify the iterators passed to cudf::detail::rolling_window.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As an aside, the compile time for rolling_fixed_window.cu has reduced from 80s to 65s.

So @divyegala's instinct was right, to keep an eye on the compile times. I think I've mitigated it for now.

@mythrocks
Copy link
Contributor Author

Ah, I haven't updated the header yet. Will do shortly.

@mythrocks
Copy link
Contributor Author

We need to update https://github.com/rapidsai/cudf/blob/branch-23.10/cpp/include/cudf/rolling.hpp#L181 to document the relaxation of preceding_window and following_window

I've updated the docs in rolling.hpp.

@mythrocks
Copy link
Contributor Author

/ok to test

@mythrocks
Copy link
Contributor Author

/merge

@rapids-bot rapids-bot bot merged commit ec744de into rapidsai:branch-23.10 Sep 21, 2023
57 of 58 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CMake CMake build issue feature request New feature or request libcudf Affects libcudf (C++/CUDA) code. non-breaking Non-breaking change Spark Functionality that helps Spark RAPIDS
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

3 participants