Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

bpo-45829: Specialize BINARY_SUBSCR for __getitem__ implemented in Python.#29592

Merged
markshannon merged 7 commits into
python:mainpython/cpython:mainfrom
faster-cpython:specialize-for-getitemfaster-cpython/cpython:specialize-for-getitemCopy head branch name to clipboard
Nov 18, 2021
Merged

bpo-45829: Specialize BINARY_SUBSCR for __getitem__ implemented in Python.#29592
markshannon merged 7 commits into
python:mainpython/cpython:mainfrom
faster-cpython:specialize-for-getitemfaster-cpython/cpython:specialize-for-getitemCopy head branch name to clipboard

Conversation

@markshannon

@markshannon markshannon commented Nov 17, 2021

Copy link
Copy Markdown
Member

Specializes BINARY_SUBSCR for classes with a __getitem__ method implemented in Python by making the call directly in the instruction using the same mechanism as CALL_FUNCTION_PY_SIMPLE.

A respectable 1% speedup including a 20% speedup for one benchmark that makes heavy use of __getitem__.

https://bugs.python.org/issue45829

@brandtbucher brandtbucher left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really cool! A few notes:

@@ -0,0 +1,2 @@
Specialize BINARY_SUBSCR for classes with a ``__getitem__`` method

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Specialize BINARY_SUBSCR for classes with a ``__getitem__`` method
Specialize :opcode:`BINARY_SUBSCR` for classes with a ``__getitem__`` method

Comment thread Python/ceval.c Outdated
Comment on lines +2091 to +2092
assert(cache->adaptive.original_oparg == 0);
oparg = 0;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just to clarify that the oparg is still unused (even though we use cache entries)?

Comment thread Python/specialize.c Outdated
int flags = code->co_flags;
if (flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) {
return SPEC_FAIL_GENERATOR;
return -1;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return -1;

Comment thread Python/specialize.c Outdated
if (code->co_nfreevars) {
return SPEC_FAIL_FREE_VARS;
}
return 0;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really like this, since 0 is a valid failure code. Perhaps return -1 on success and check for that instead (it's sort of a weird interface, but whatever).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a success code, for clarity.

In many other C programs, yes 0 is a failure.
However, in CPython, 0 is usually a success, as -1 (or any negative number in some cases) is a failure.
E.g. PyObject_RichCompareBool return 0 for False, and -1 for an error.

compile.c is an unfortunate counter example, where some functions return 0 for a failure and others return 0 for a success.

Comment thread Python/specialize.c
_Py_IDENTIFIER(__getitem__);

static int
function_kind(PyCodeObject *code) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function_kind(PyCodeObject *code) {
function_spec_fail_kind(PyCodeObject *code) {

Comment thread Python/ceval.c
MISS_WITH_CACHE(CALL_FUNCTION)
MISS_WITH_CACHE(BINARY_OP)
MISS_WITH_OPARG_COUNTER(BINARY_SUBSCR)
MISS_WITH_CACHE(BINARY_SUBSCR)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing it's still worth keeping the logic for oparg counters, just in case we implement a new family that can uses it in the future?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might as well delete it. It's in the git history.

@Fidget-Spinner Fidget-Spinner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the same questions as Brandt, but otherwise this LGTM.

While not in pyperformance, I'm excited for code using typing, since that module heavily uses __getitem__ (and __class_getitem__). Awesome!

@markshannon

markshannon commented Nov 18, 2021

Copy link
Copy Markdown
Member Author

I have the same questions as Brandt, but otherwise this LGTM.

While not in pyperformance, I'm excited for code using typing, since that module heavily uses __getitem__ (and __class_getitem__). Awesome!

Type hints are run-once code (at least they should be) so won't be affected.

@markshannon markshannon merged commit 21fa7a3 into python:main Nov 18, 2021
@Fidget-Spinner

Copy link
Copy Markdown
Member

Type hints are run-once code (at least they should be) so won't be affected.

Agreed that it's true for the vast majority. But recently I noticed variable annotations sometimes appear in hot code paths and they might be affected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.