-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add Glushkov regex fast-path to libcudf #21936
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
Changes from 96 commits
Commits
Show all changes
97 commits
Select commit
Hold shift + click to select a range
b39a18e
feature: implement Glushkov's NFA
lingyany-nv d90a5e6
implement shared memory version of Glushkov's NFA
lingyany-nv 7644ff4
refactor and simplify the code
lingyany-nv b3d7ab9
add python regex tests from spark-rapids regex tests
lingyany-nv cca1cba
add more complex regexes to the benchmarks
lingyany-nv 8cacf16
fix the correctness (leftmost-longest vs leftmost-first) issue; chang…
lingyany-nv 76fb67f
add compile-time-check to disalbe Glushkov when it cannot produce lef…
lingyany-nv cca35f9
add nullable regexes unit tests
lingyany-nv 9edbab7
for affected benchmarks, keep unsupported patterns in the benchmark, …
lingyany-nv f2240da
fix minor issues
lingyany-nv 98646b4
delete some dead code, update stale comments
lingyany-nv 2eaec3c
remove stalbe comments
lingyany-nv 30fa2eb
applied the formatter
lingyany-nv b114480
fix python format
lingyany-nv 8acb318
fix clang-format again
lingyany-nv c4e1cb8
add replace_re tests, fixed minior issues
lingyany-nv 7c4fc14
clang-format
lingyany-nv c18abd8
deleted unuseful comments
lingyany-nv 604b216
remove multi-pattern replace
davidwendt 2e3a089
remove multi_re.cu from cmake file
davidwendt 6328c9e
remove nullable, lazy-quantifier; add tests
davidwendt 68bd0f4
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 324b304
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt e642ea1
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt d635a72
use non-capture on findall tests
davidwendt 93fcf31
fix merge conflicts
davidwendt 5f7bab7
use reinst for positions
davidwendt 2c119e5
remove glushkov.inl
davidwendt b1ebcd7
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt cf49828
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt e3ae0a0
remove unused glushkov_global_source
davidwendt 24163c5
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt c2b29aa
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 2ca6b3b
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 1a6341b
fix merge conflicts
davidwendt 08736b4
factor out useful tests appropriately
davidwendt 3a17319
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt d415848
merge changes for findall_tests.cpp
davidwendt 5a2228b
templatize prog_device in utilities.cuh functions
davidwendt e06ca9d
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 22e7b81
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 1ab8b6e
removed more code; change g_state to glushkov_state
davidwendt f59edb4
separated gkprog_device from reprog_device
davidwendt c29f286
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 6a11a35
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt d9ba38d
some cleanup of comments and member variable names
davidwendt 03ad2da
rework gkprog_device::create
davidwendt 7a3b5c2
fix merge conflict
davidwendt 4a0df6a
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 53bb5cc
remove the GLUSHKOV regex-flag
davidwendt 4dd07ed
add glushkov fast-path to appropriate regex functions
davidwendt b7f1b50
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 7d8e573
remove glushkov_tests.cpp
davidwendt 6f7d705
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 73a179e
replace __builtins with STL calls
davidwendt e177089
reset regecomp.cpp
davidwendt 3d3e50e
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 0999669
linter fix: auto cast
davidwendt b6aebfb
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 84af963
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 5810765
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 5ab9b4f
cleanup and rename reclass.hpp to common.cuh
davidwendt ef1376f
add env variable
davidwendt 5535e37
undo unneeded benchmark changes
davidwendt bcbf166
add sync calls
davidwendt 28d11f5
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt bdf6185
remove unneeded variable
davidwendt 11f77ad
prevent double creating the reprog-device objects
davidwendt 5189fe1
fix builtins space check
davidwendt d9ff906
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 38f856b
fix new linter error
davidwendt 92cd568
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt e1d7ec5
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 5d18241
remove nullable words
davidwendt 7efb37f
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 7ae1f3c
use stack intead of recursion for eps_closure and ordered_eps_frontier
lingyany-nv 9d6a4ca
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 2891dcc
remove new; rework reclass
davidwendt 9046fdf
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 92e93d5
undo comment change
davidwendt 35290f6
undo more unneeded changes to regexec.cpp
davidwendt 7047521
remove unneeded include
davidwendt 01e844e
added some consts
davidwendt c848b9d
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 9d2570e
use tuple as return type, use array instead of map
lingyany-nv 293d238
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 251211d
Merge branch 'lingyany/glushkov-nfa' of github.com:lingyany-nv/cudf i…
davidwendt 8a46843
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt ae3bdbc
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 7179ad7
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 41caa42
replace some literals with named constant
davidwendt cc0e60a
remove include cuda_runtime.h
davidwendt 54af99b
add/update some comments
davidwendt 8eaf307
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt ed12956
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt 0e54aa9
Merge branch 'main' into lingyany/glushkov-nfa
davidwendt faf578d
fix merge conflicts
davidwendt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,20 @@ | ||
| /* | ||
| * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION. | ||
| * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #include "strings/count_matches.hpp" | ||
| #include "strings/regex/regex_program_impl.h" | ||
| #include "strings/regex/utilities.cuh" | ||
|
|
||
| #include <cudf/column/column_device_view.cuh> | ||
| #include <cudf/column/column_factories.hpp> | ||
| #include <cudf/strings/regex/regex_program.hpp> | ||
| #include <cudf/strings/string_view.cuh> | ||
| #include <cudf/utilities/memory_resource.hpp> | ||
|
|
||
| #include <type_traits> | ||
|
|
||
| namespace cudf { | ||
| namespace strings { | ||
| namespace detail { | ||
|
|
@@ -23,8 +27,9 @@ template <positional P> | |
| struct count_fn { | ||
| column_device_view const d_strings; | ||
|
|
||
| template <typename ProgDevice> | ||
| __device__ int32_t operator()(size_type const idx, | ||
| reprog_device const prog, | ||
| ProgDevice const prog, | ||
| int32_t const thread_idx) | ||
| { | ||
| if (d_strings.is_null(idx)) return 0; | ||
|
|
@@ -34,7 +39,7 @@ struct count_fn { | |
|
|
||
| auto itr = d_str.begin(); | ||
| while (itr.position() <= nchars) { | ||
| auto result = prog.find<P>(thread_idx, d_str, itr); | ||
| auto result = prog.template find<P>(thread_idx, d_str, itr); | ||
| if (!result) { break; } | ||
| ++count; | ||
| // increment the iterator is faster than creating a new one | ||
|
|
@@ -47,29 +52,64 @@ struct count_fn { | |
|
|
||
| } // namespace | ||
|
|
||
| template <typename ProgDevice> | ||
| std::unique_ptr<column> count_matches(column_device_view const& d_strings, | ||
| reprog_device& d_prog, | ||
| ProgDevice& d_prog, | ||
| size_type strings_count, | ||
| rmm::cuda_stream_view stream, | ||
| rmm::device_async_resource_ref mr) | ||
| { | ||
| auto results = make_numeric_column( | ||
| data_type{type_to_id<size_type>()}, d_strings.size(), mask_state::UNALLOCATED, stream, mr); | ||
| data_type{type_to_id<size_type>()}, strings_count, mask_state::UNALLOCATED, stream, mr); | ||
|
|
||
| if (d_strings.size() == 0) { return results; } | ||
| if (strings_count == 0) { return results; } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we need this branch?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, otherwise we could launch a kernel with no threads. |
||
|
|
||
| auto d_results = results->mutable_view().data<cudf::size_type>(); | ||
|
|
||
| if (d_prog.is_empty_match_possible()) { | ||
| // Glushkov's engine always requires the begin/end positional check; the Thompson | ||
| // engine can skip it (cheaper) when an empty match is not possible for this pattern. | ||
| if constexpr (std::is_same_v<ProgDevice, gkprog_device>) { | ||
| launch_transform_kernel( | ||
| count_fn<positional::BEGIN_END>{d_strings}, d_prog, d_results, d_strings.size(), stream); | ||
| count_fn<positional::BEGIN_END>{d_strings}, d_prog, d_results, strings_count, stream); | ||
| } else { | ||
| launch_transform_kernel( | ||
| count_fn<positional::END_ONLY>{d_strings}, d_prog, d_results, d_strings.size(), stream); | ||
| if (d_prog.is_empty_match_possible()) { | ||
| launch_transform_kernel( | ||
| count_fn<positional::BEGIN_END>{d_strings}, d_prog, d_results, strings_count, stream); | ||
| } else { | ||
| launch_transform_kernel( | ||
| count_fn<positional::END_ONLY>{d_strings}, d_prog, d_results, strings_count, stream); | ||
| } | ||
| } | ||
|
|
||
| return results; | ||
| } | ||
|
|
||
| template std::unique_ptr<column> count_matches<reprog_device>(column_device_view const&, | ||
| reprog_device&, | ||
| size_type, | ||
| rmm::cuda_stream_view, | ||
| rmm::device_async_resource_ref); | ||
|
|
||
| template std::unique_ptr<column> count_matches<gkprog_device>(column_device_view const&, | ||
| gkprog_device&, | ||
| size_type, | ||
| rmm::cuda_stream_view, | ||
| rmm::device_async_resource_ref); | ||
|
|
||
| std::unique_ptr<column> count_matches(column_device_view const& d_strings, | ||
| regex_program const& prog, | ||
| rmm::cuda_stream_view stream, | ||
| rmm::device_async_resource_ref mr) | ||
| { | ||
| auto const strings_count = d_strings.size(); | ||
| if (regex_device_builder::glushkov_fast_path_supported(prog)) { | ||
| auto d_prog = regex_device_builder::create_gkprog_device(prog, stream); | ||
| return count_matches(d_strings, *d_prog, strings_count, stream, mr); | ||
| } | ||
| auto d_prog = regex_device_builder::create_prog_device(prog, stream); | ||
| return count_matches(d_strings, *d_prog, strings_count, stream, mr); | ||
| } | ||
|
|
||
| } // namespace detail | ||
| } // namespace strings | ||
| } // namespace cudf | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| /* | ||
| * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| #pragma once | ||
|
|
||
| #include "regcomp.h" | ||
|
|
||
| #include <cudf/strings/detail/char_tables.hpp> | ||
| #include <cudf/strings/detail/utf8.hpp> | ||
| #include <cudf/types.hpp> | ||
|
|
||
| #include <cuda/std/optional> | ||
| #include <cuda/std/utility> | ||
| #include <thrust/execution_policy.h> | ||
| #include <thrust/logical.h> | ||
|
|
||
| namespace cudf::strings::detail { | ||
|
|
||
| /// Bitmask type: bit i is set when Glushkov position i is active. | ||
| using glushkov_state_t = uint64_t; | ||
|
|
||
| /// Maximum number of character-consuming positions (states) in the Glushkov NFA. | ||
| /// Patterns with more positions fall back to Thompson NFA automatically. | ||
| constexpr int32_t GLUSHKOV_MAX_STATES = sizeof(glushkov_state_t) * 8; | ||
|
|
||
| /// Maximum shift amounts for the Hyperscan-style shift-and optimization. | ||
| constexpr int32_t GLUSHKOV_MAX_SHIFTS = 8; | ||
|
|
||
| /// Size of the precomputed ASCII reach table (characters 0–127). | ||
| constexpr int32_t GLUSHKOV_ASCII_TABLE_SIZE = 128; | ||
|
PointKernel marked this conversation as resolved.
|
||
|
|
||
| /** | ||
| * @brief Regex class stored on the device and executed by reprog_device. | ||
| * | ||
| * This class holds the unique data for any regex CCLASS instruction. | ||
| */ | ||
| struct alignas(16) reclass_device { | ||
| int32_t builtins{}; | ||
| int32_t count{}; | ||
| reclass_range const* literals{}; | ||
|
|
||
| __device__ inline bool is_match(char32_t const ch, uint8_t const* codepoint_flags) const | ||
| { | ||
| if (thrust::any_of(thrust::seq, literals, literals + count, [ch](auto literal) { | ||
| return ((ch >= literal.first) && (ch <= literal.last)); | ||
| })) { | ||
| return true; | ||
| } | ||
|
|
||
| if (!builtins) { return false; } | ||
| auto const codept = utf8_to_codepoint(ch); | ||
| constexpr uint32_t MAX_CODEPOINT = 0x00'FFFF; | ||
| if (codept > MAX_CODEPOINT) { return false; } | ||
| auto const fl = codepoint_flags[codept]; | ||
| if ((builtins & CCLASS_W) && ((ch == '_') || IS_ALPHANUM(fl))) { return true; } // \w | ||
| if ((builtins & CCLASS_S) && IS_SPACE(fl)) { return true; } // \s | ||
| if ((builtins & CCLASS_D) && IS_DIGIT(fl)) { return true; } // \d | ||
| if ((builtins & NCCLASS_W) && ((ch != '\n') && (ch != '_') && !IS_ALPHANUM(fl))) { // \W | ||
| return true; | ||
| } | ||
| if ((builtins & NCCLASS_S) && !IS_SPACE(fl)) { return true; } // \S | ||
| if ((builtins & NCCLASS_D) && ((ch != '\n') && !IS_DIGIT(fl))) { return true; } // \D | ||
|
|
||
| return false; | ||
| } | ||
| }; | ||
|
|
||
| /** | ||
| * @brief Check for supported new-line characters | ||
| * | ||
| * '\n, \r, \u0085, \u2028, or \u2029' | ||
| */ | ||
| CUDF_HOST_DEVICE __forceinline__ constexpr bool is_newline(char32_t const ch) | ||
| { | ||
| return (ch == '\n' || ch == '\r' || ch == 0x00c285 || ch == 0x00e280a8 || ch == 0x00e280a9); | ||
| } | ||
|
|
||
| /** | ||
| * @brief Template type used on `find` to specify desired position values in returned match_result | ||
| */ | ||
| enum class positional : int8_t { | ||
| BEGIN_END = 0, /// both begin and end positions are returned | ||
| END_ONLY = 1, /// only the end position is returned | ||
| }; | ||
|
|
||
| using match_pair = cuda::std::pair<cudf::size_type, cudf::size_type>; | ||
| using match_result = cuda::std::optional<match_pair>; | ||
|
PointKernel marked this conversation as resolved.
|
||
|
|
||
| } // namespace cudf::strings::detail | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Should we take this chance migrating all
.hheaders touched in this PR to.hppto align with the libcudf convention?Uh oh!
There was an error while loading. Please reload this page.
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.
Again, this is not a new file. I don't think this kind of change should be included in this PR.
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'm happy with that. As a small suggestion, would it make sense for new headers like
glushkov_regcomp.hto use the.hppextension?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'm favorable towards changing this. If not in this PR, in an immediate follow-up.