-
Notifications
You must be signed in to change notification settings - Fork 51
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
Speed up s.startswith() #671
Comments
FWIW, we already do this with |
It is in the debug build, but
|
I think @JelleZijlstra showed that in a fully optimized build it is still slower. I repeated his experiments and got the same results:
EDIT: The second timing is wrong, see corrected timing below. |
Your second timing does both the slice and method call. :) |
Oof. :-( But it's still slower: 40 vs. 32:
|
Yes, for comparison with #671 (comment), on the same computer:
The difference is larger for 1-character prefix (because the overhead of making a 1-character substring is much smaller):
|
There is also some overhead in the argument parsing for
Adding a simple fast path for the single argument case of
The fast path I implemented is python/cpython@main...eendebakpt:cpython:startswith. That might not be the best approach, but this was just a quick test. The |
This is not true. It is the call that is slow, not the attribute lookup. The attribute lookup gets specialized to The call is slow for two reasons.
def foo(s):
for _ in range(1000):
s.startswith("foo")
|
We should probably replace all In the short term, we should change the methods on builtin objects. |
|
@eendebakpt Can you turn that into a PR then? |
Yes, I will pick that up @erlend-aasland Did you use argument clinic in testing the METH_FASTCALL? |
@eendebakpt: yes. See my |
Resolved by:
Argument Clinic (and METH_FASTCALL) FTW. |
@eendebakpt is also working on further optimisations in stringlib 🚀 |
With the recent conversion to vectorcall by Erland there is not much to gain in the implementation of
The implementation of the actual string comparison can be improved a bit, for the single character case a few ns. See #117782
Our baseline is `x.startswith('a')` which takes about 37.6 ns:
```
x.startswith('a'): Mean +- std dev: 37.6 ns +- 0.1 ns
```
That time can be roughly divided into:
at the start of
at the start of
and comparing to the previous.
Performance of
The performance gain is not very large, but |
People keep discovering that, embarrassingly, checking whether a string starts with a given substring is slower with
s.startswith("foo")
than withs[:3] == "foo"
. The reason is thatstartswith
requires a name lookup. I think we now have the machinery to special-case selected built-in method in the specializer? This would be a good candidate.The text was updated successfully, but these errors were encountered: