You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
### Add bit-parallel Glushkov NFA regex engine with shared memory optimization
Implement Glushkov's NFA for regex string matching in libcudf: references (1) [hyperscan paper](https://www.usenix.org/system/files/nsdi19-wang-xiang.pdf) (2) [HybridSA paper](https://dl.acm.org/doi/10.1145/3689771) (3) [vectorscan repo](https://github.com/Vectorcamp/vectorscan).
Basically, this is Glushkov's NFA compared with the other popular Thompson's NFA (also used in current libcudf regex). The Glushkov engine represents NFA state as a single uint64_t bitmask (max 64 positions), requiring no global memory per thread. Shared memory is used to hold the static instructions like in the current implementation.
### Key changes
- Two-phase O(n) unanchored search algorithm (glushkov.inl): Phase 1 scans forward, injecting start states each character and recording provisional match ends. Phase 2 rescans only the match region to find the true leftmost start. Each character is processed at most twice.
- Leftmost-first correctness via priority-kill (glushkov.cuh, glushkov_regcomp.cpp): A runtime glushkov_priority_kill clears lower-priority alternative paths at accept time. A compile-time conflict detector (frontier_has_priority_conflict) conservatively falls back to Thompson when bit-index ordering cannot guarantee Thompson-compatible leftmost-first semantics.
- Automatic fallback: Patterns with anchors (^, $, \b, \B), >64 positions, match empty top-level expressions, capture group requirements (extract, backref_re), or priority conflicts transparently fall back to Thompson NFA — no user intervention needed.
### Limitations
- does not support capturing groups (e.g. extract, extract_all, findall, replace_with_backrefs)
- does not support zero-width assertions (empty-matchable) like BOL/EOL/BOW/NBOW
- max 64 character-consuming positions since we are using uint64_t as state data per row/thread
- does not support lazy quantifiers
- empty/degenerate patterns rejected
- does not support empty-matchable patterns as well as some ambiguous alternation patterns
When above condition is detected, it falls back to use the current Thompson's NFA.
### Unit tests + benchmark
- Priority-kill parity tests: Verify Glushkov matches Thompson for overlapping-prefix alternations (foo|foobar, cat|catch, a|aa) across all 5 operations (contains, count, findall, replace, split)
- Empty-matchable fallback parity: Confirm nullable patterns (a*, \d*, (ab)?) transparently fall back to Thompson and produce identical results
- Spark-rapids compatibility: ~60 regex patterns from spark-rapids integration tests validated under both engines via parametrized Python tests
- Benchmarks: 6–9 patterns per benchmark covering char classes, alternation, bounded repetition, dot wildcards, and late-failure stress patterns
- Extended more complex regexes in the current split_re/contains/replace_re/count, it showed 1.01-6.62x speedup.
Authors:
- Lingyan Yin (https://github.com/lingyany-nv)
- David Wendt (https://github.com/davidwendt)
Approvers:
- Basit Ayantunde (https://github.com/lamarrr)
- Yunsong Wang (https://github.com/PointKernel)
- Bradley Dice (https://github.com/bdice)
URL: #21936
0 commit comments