Repository navigation
[unicode-PERF]: use optmized BM algorithm to replace the brute-force finder - #8626
Conversation
|
FYI,
|
guilhermeleobas
left a comment
There was a problem hiding this comment.
Hi @dlee992, thanks for working on this. I've done an initial review, but the code looks pretty similar to the one implemented in CPython.
Question: Have you measure the time using the new algorithm vs the older implementation?
Yeah. I did copy that piece of CPython code and modify based on it, and I add the code link in the comments. I think CPython impl should be a best practice, and Numba
No, not yet. I can work on that, make up some test cases to see the time difference. Will update it here when the perf test is ready. |
|
Here's a made-up test case, copied from the method Details def test_find_perf(self):
pyfunc = find_usecase
cfunc = njit(pyfunc)
# pre-run cfunc
cfunc("abc", "a")
default_subs = [
(s, ['', 'xx', s[:-2], s[3:], s]) for s in UNICODE_EXAMPLES
]
cpython_subs = [
('a' * 100 + '\u0102', ['\u0102', '\u0201', '\u0120', '\u0220']),
('a' * 100 + '\U00100304', ['\U00100304', '\U00100204',
'\U00102004']),
('\u0102' * 100 + 'a', ['a']),
('\U00100304' * 100 + 'a', ['a']),
('\U00100304' * 100 + '\u0102', ['\u0102']),
('a' * 100, ['\u0102', '\U00100304', 'a\u0102', 'a\U00100304']),
('\u0102' * 100, ['\U00100304', '\u0102\U00100304']),
('\u0102' * 100 + 'a_', ['a_']),
('\U00100304' * 100 + 'a_', ['a_']),
('\U00100304' * 100 + '\u0102_', ['\u0102_']),
]
start_time = time.time()
for _ in range(1000):
for s, subs in default_subs + cpython_subs:
for sub_str in subs:
cfunc(s, sub_str)
print(f"consumed time in short test cases: {time.time() - start_time}")
# made-up cases for fast search
for leng in [10, 100, 1000, 10000]:
s = "abcdefg" * leng + "hijk"
sub_str = "hijk"
start_time = time.time()
for _ in range(1000):
cfunc(s, sub_str)
print(f"consumed time with len={len(s)}: {time.time() - start_time}")Consumed time in Numba 0.56.3: consumed time in short test cases: 0.19890618324279785
consumed time with len=74: 0.003802061080932617
consumed time with len=704: 0.006798982620239258
consumed time with len=7004: 0.03879284858703613
consumed time with len=70004: 0.44359922409057617Consumed time in Numba main with this PR: total time consumed: 0.18944501876831055
consumed time with len=74: 0.004054069519042969
consumed time with len=704: 0.003863811492919922
consumed time with len=7004: 0.007459163665771484
consumed time with len=70004: 0.04593682289123535In short test cases, consumed time are similar (perhaps a minor degradation with this PR, but marginal, I think). |
Can you share the source code for these? |
I have already shared the code, u can unfold the |
|
Oh, sorry. I'll check it tomorrow morning. |
|
Thanks @dlee992. I ran the code and your approach is way faster than the old one: # your branch
consumed time in short test cases: 0.1025538444519043
consumed time with len=74: 0.0019850730895996094
consumed time with len=704: 0.002277851104736328
consumed time with len=7004: 0.006390094757080078
consumed time with len=70004: 0.04852104187011719
consumed time with len=700004: 0.45890283584594727
consumed time with len=7000004: 4.588933944702148
# Numba main
consumed time in short test cases: 0.10606002807617188
consumed time with len=74: 0.002021312713623047
consumed time with len=704: 0.003590106964111328
consumed time with len=7004: 0.01938009262084961
consumed time with len=70004: 0.17744994163513184
consumed time with len=700004: 1.744581937789917
consumed time with len=7000004: 17.453103065490723 |
|
Hi, @guilhermeleobas and @sklam Since no obvious side effects with this PR, I feel this one can also be put into Numba 0.57 RC if possible. |
|
Yes! I just tagged to 0.57 |
Hi, after some digging, I found cpython has at least 3 ways to tackle string-matching operations, based on some metrics on
needleandhaystacklengths:find_char: usememchras possible as we can, which using hand-tuned assembly code with vectorized instructions;default_find: useBoyer-Moorealgorithm withHorspool(O(1)space) andSunday(bloom filter) optimizations (merged in 2006);_two_way_find: a more advanced but longer preprocessing algorithm (merged in 2021).Indeed, I haven't known how to implement a vectorized find for char search (any idea? I would like to contribute, but don't know exactly the details), so right now I just implemented the second algorithm for numba default string find underlying impl.
I haven't perf the speedup ratio, but choose to trust cpython's decision. If needed, I could make up some perf tests to show how much reduced comparsions w/ this PR.
PS, during the development, I found numba doesn't support python
reversedkeyword, is this owing to some numba limitations? Or we just didn't have time to implement it?