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

Remove _get_[both]sep[s]() calls in os.path #117634

Closed
nineteendo opened this issue Apr 8, 2024 · 10 comments
Closed

Remove _get_[both]sep[s]() calls in os.path #117634

nineteendo opened this issue Apr 8, 2024 · 10 comments
Labels
performance Performance or resource usage stdlib Python modules in the Lib dir type-feature A feature request or enhancement

Comments

@nineteendo
Copy link
Contributor

nineteendo commented Apr 8, 2024

Feature or enhancement

Proposal:

Python function calls are always expensive. We can replace all _get_sep() & _get_bothseps() calls in os.path with ternary operators, merging with existing isinstance() checks where applicable. e.g:

-seps = _get_bothseps(p)
+seps = b'\\/' if isinstance(p, bytes) else '\\/'

Also, in posixpath.expanduser() we can replace root with the already assigned sep from earlier:

if isinstance(path, bytes):
    userhome = os.fsencode(userhome)
-    root = b'/'
-else:
-    root = '/'
-userhome = userhome.rstrip(root)
-return (userhome + path[i:]) or root
+userhome = userhome.rstrip(sep)
+return (userhome + path[i:]) or sep

Has this already been discussed elsewhere?

This is a minor feature, which does not need previous discussion elsewhere

Links to previous discussion of this feature:

Linked PRs

@nineteendo nineteendo added the type-feature A feature request or enhancement label Apr 8, 2024
@erlend-aasland
Copy link
Contributor

Python function calls are always expensive.

This is being worked on:

@nineteendo
Copy link
Contributor Author

OK, but calling a function will always have some overhead, we can never fully eliminate that (unless we add macro's to python).
And the code will still fit on a single line, making the intention much clearer:

-seps = _get_bothseps(p)
+seps = b'\\/' if isinstance(p, bytes) else '\\/'

@AlexWaygood AlexWaygood added performance Performance or resource usage stdlib Python modules in the Lib dir labels Apr 8, 2024
@terryjreedy
Copy link
Member

Why do you think that the isinstance call faster than the _get_bothseps call? Removing a redundant call is another matter.

@eryksun
Copy link
Contributor

eryksun commented Apr 8, 2024

@terryjreedy, _get_bothseps() calls isinstance(). The suggestion is to manually inline the code in the source at each call site since Python's compiler doesn't support inline function calls. It could be considered unnecessary optimization, however. Call overhead of a few nanoseconds is a drop in the bucket compared to work that requires a few microseconds.

@nineteendo
Copy link
Contributor Author

It could be considered unnecessary optimization, however. Call overhead of a few nanoseconds is a drop in the bucket compared to work that requires a few microseconds.

In my benchmarks, the new code is 4-12% faster: #117635 (comment). If you want benchmarks for longer input, let me know.

Also, ntpath.expanduser() calls this function in every iteration of a loop, without saving the result:

cpython/Lib/ntpath.py

Lines 377 to 378 in e338e1a

while i < n and path[i] not in _get_bothseps(path):
i += 1

Doing that makes it 61% faster for long user names (>100 characters).

@erlend-aasland
Copy link
Contributor

erlend-aasland commented Apr 9, 2024

I'm not sure the performance win outweights the increase in code duplication.

@nineteendo
Copy link
Contributor Author

nineteendo commented Apr 9, 2024

Do I need to make a separate issue for os.path.expanduser() (on posix we're doing a redundant isinstance() check) or can I also change the other functions?

@erlend-aasland
Copy link
Contributor

As I've said earlier, micro-optimisations are generally hard to get support for; we mostly prefer code clarity, readability and maintainability over performance.

I would instead encourage you to direct your enthusiasm towards the existing issues in the bug-tracker. There's close to 7000 issues in the tracker. What we really need help with, is triaging this heap of issues, fixing those who can be fixed, closing those who are invalid, etc. I think you could be of real help (and you would experience more support from the core team) if you directed your skills towards the bug-tracker.

@nineteendo
Copy link
Contributor Author

I've made a separate issue for ntpath.expanduser(): #117686 We really shouldn't call _get_bothseps() in every iteration.
I've also created 5 pull requests for existing issues in the issue tracker: https://github.com/python/cpython/pulls/nineteendo.
So if you want, you can review them.

@erlend-aasland
Copy link
Contributor

I've made a separate issue for ntpath.expanduser(): #117686 We really shouldn't call _get_bothseps() in every iteration.

That's seems a fair point. I suggest to close this issue, though; IMO the performance gains do not outweigh the added code duplication nor the churn.

I've also created 5 pull requests for existing issues in the issue tracker: https://github.com/python/cpython/pulls/nineteendo. So if you want, you can review them.

That's great; thanks for helping reducing our backlog!

@erlend-aasland erlend-aasland closed this as not planned Won't fix, can't repro, duplicate, stale Apr 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance Performance or resource usage stdlib Python modules in the Lib dir type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

5 participants