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

bpo-42927: Inline cache for slots #24216

Merged
merged 7 commits into from
Jan 30, 2021
Merged

Conversation

gvanrossum
Copy link
Member

@gvanrossum gvanrossum commented Jan 14, 2021

This is a modification of the LOAD_ATTR inline cache that landed a few months ago (PR 22803).

The design overloads the hint field: if it is negative, it indicates a slot. Slots are created using __slots__, but some builtin types that use PyMemberDescrObject descriptors (with the type set to T_OBJECT_EX) will also benefit.

I've tried to keep the code style similar to the existing LOAD_ATTR cache (happy path first) but I had to make some compromises, e.g. the cache initialization code can no longer skip everything if tp_dictoffset is zero (since it will be zero for classes with only slots).

https://bugs.python.org/issue42927

Python/ceval.c Outdated Show resolved Hide resolved
@vstinner
Copy link
Member

cc @methane @pablogsal

Python/ceval.c Outdated Show resolved Hide resolved
@pablogsal
Copy link
Member

pablogsal commented Jan 14, 2021

Something is wrong, we are reaching ther error label in the eval loop without an error set (check the CI). (The cache is not active in debug mode so if you want to reproduce compile without --with-pydebug). See this comment: https://github.com/python/cpython/pull/24216/files#r557650392

Python/ceval.c Outdated Show resolved Hide resolved
Python/ceval.c Outdated Show resolved Hide resolved
@gvanrossum
Copy link
Member Author

Okay, assuming the tests pass, this is the version I'm sending for review.

@gvanrossum
Copy link
Member Author

It seems test_ssl on macOS failed, but the other test failures have been resolved. Is that test flaky?

@pablogsal
Copy link
Member

It seems test_ssl on macOS failed, but the other test failures have been resolved. Is that test flaky?

Given the nature of this change and the fact that the error says:

ConnectionRefusedError: [Errno 61] Connection refused

I would say is unrelated. I have restarted manually the tests.

@pablogsal pablogsal added the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Jan 21, 2021
@bedevere-bot
Copy link

🤖 New build scheduled with the buildbot fleet by @pablogsal for commit b725a65 🤖

If you want to schedule another build, you need to add the ":hammer: test-with-buildbots" label again.

@bedevere-bot bedevere-bot removed the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Jan 21, 2021
@pablogsal
Copy link
Member

LGTM! Let's do a buildbot pass to make sure there are no reference leaks.

@pablogsal
Copy link
Member

pablogsal commented Jan 21, 2021

Seems we are missing something because some of the buildbots are crashing with segmentation faults. For instance:

https://buildbot.python.org/all/#/builders/459/builds/69

test_signature_on_decorated (test.test_inspect.TestSignatureObject) ... ok
Fatal Python error: Segmentation fault
Current thread 0x00007fff99164920 (most recent call first):
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/inspect.py", line 2304 in _signature_from_callable
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/inspect.py", line 2877 in from_callable
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/inspect.py", line 3129 in signature
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/test/test_inspect.py", line 2392 in test_signature_on_decorated_builtins
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/unittest/case.py", line 549 in _callTestMethod
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/unittest/case.py", line 592 in run
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/unittest/case.py", line 652 in __call__
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/unittest/suite.py", line 122 in run
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/unittest/suite.py", line 84 in __call__
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/unittest/suite.py", line 122 in run
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/unittest/suite.py", line 84 in __call__
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/unittest/runner.py", line 176 in run
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/test/support/__init__.py", line 959 in _run_suite
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/test/support/__init__.py", line 1082 in run_unittest
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/test/test_inspect.py", line 4062 in test_main
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/test/libregrtest/runtest.py", line 236 in _runtest_inner2
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/test/libregrtest/runtest.py", line 272 in _runtest_inner
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/test/libregrtest/runtest.py", line 155 in _runtest
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/test/libregrtest/runtest.py", line 195 in runtest
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/test/libregrtest/main.py", line 319 in rerun_failed_tests
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/test/libregrtest/main.py", line 696 in _main
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/test/libregrtest/main.py", line 639 in main
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/test/libregrtest/main.py", line 717 in main
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/test/__main__.py", line 2 in <module>
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/runpy.py", line 87 in _run_code
  File "/home/buildbot/buildarea/pull_request.cstratak-RHEL8-ppc64le.lto-pgo/build/Lib/runpy.py", line 197 in _run_module_as_main
Extension modules: _testcapi (total: 1)
make: *** [Makefile:1228: buildbottest] Segmentation fault (core dumped)
program finished with exit code 2
elapsedTime=259.101476

The failures seem to be in test_inspect and test_zipfile

@gvanrossum
Copy link
Member Author

Hm. I think this may happen when _PyDict_GetItemHint() returns -1. Then we set la->hint = -1 and then the next time we get here it is interpreted as a slot hint. I'll come up with a fix.

The problem was that sometimes a hint of -1 could be set, meaning the
dict lookup failed.  My code mistook that for a slot offset.

The fix is simple, because slot offsets can never be 0, so ~offset
can never be -1.

I also cleaned up some comments and a variable name.
@gvanrossum
Copy link
Member Author

Here's the fix. @pablogsal how do I trigger a buildbot run? I can't find that in the devguide.

@pablogsal
Copy link
Member

Here's the fix. @pablogsal how do I trigger a buildbot run? I can't find that in the devguide.

You can add the "run with buildbots" label to the PR and the checks should start appearing at the bottom together with Travis and the other CI.

@pablogsal pablogsal added the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Jan 28, 2021
@bedevere-bot
Copy link

🤖 New build scheduled with the buildbot fleet by @pablogsal for commit 4c2eb69 🤖

If you want to schedule another build, you need to add the ":hammer: test-with-buildbots" label again.

@bedevere-bot bedevere-bot removed the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Jan 28, 2021
@gvanrossum gvanrossum added the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Jan 28, 2021
@bedevere-bot
Copy link

🤖 New build scheduled with the buildbot fleet by @gvanrossum for commit 17c8a95 🤖

If you want to schedule another build, you need to add the ":hammer: test-with-buildbots" label again.

@bedevere-bot bedevere-bot removed the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Jan 28, 2021
// Hint >= 0 is a dict index; hint < 0 is an inverted slot index.
if (la->hint < 0) {
/* Even faster path -- slot hint */
// Hint >= 0 is a dict index; hint == -1 is a dict miss.
Copy link
Member

Choose a reason for hiding this comment

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

Oh, right! How did I miss that on review :/

Copy link
Member Author

Choose a reason for hiding this comment

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

Same way I missed it writing the code. :-(

This code is hairy.

Python/ceval.c Outdated
if (la->hint < 0) {
/* Even faster path -- slot hint */
// Hint >= 0 is a dict index; hint == -1 is a dict miss.
// Hint < -1 is an inverted slot offset (offsets are never 0).
Copy link
Member

Choose a reason for hiding this comment

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

Maybe clarify that slot offsets are always greater than 1?

Copy link
Member Author

Choose a reason for hiding this comment

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

I have a comment ready to push:

                        // Hint >= 0 is a dict index; hint == -1 is a dict miss.
                        // Hint < -1 is an inverted slot offset: offset is strictly > 0,
                        // so ~offset is strictly < -1 (assuming 2's complement).

Let me know if you still want improvement.

Copy link
Member

Choose a reason for hiding this comment

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

looks good to me

@gvanrossum
Copy link
Member Author

Looks like only 2 buildbots failed.

  • AMD64 FreeBSD Shared PR: Crash in test_gdb with Exception: Command 'gdb -nx --version' failed with exit code 1: stdout='' stderr='ld-elf.so.1: Shared object "libncursesw.so.6" not found, required by "gdb"\n'; also failing are test_subprocess (another libcurses issue) and test_venv (test_deactivate_with_strict_bash_opts fails with a subprocess error).

  • s390x Fedora Refleaks PR: test_curses failed in test_init_pair. Do we know if that passes for master?

This seems a better result than before.

@gvanrossum
Copy link
Member Author

I realized there's another optimization for this case. The code always starts with PyObject *name = GETITEM(names, oparg); which calls PyTuple_GetItem(). But if the fast slot/field path is taken we never need that! However the fast dict hint path does need it, and of course all slow paths need it. So the logic for deciding to get this would be convoluted (it would have to be in various else branches).

Is that worth exploring? It might be hard to prove there's no slowdown for the paths that do need the name. I could put this off to another PR -- if slots become more popular because of this PR we can add it later. :-)

@gvanrossum
Copy link
Member Author

gvanrossum commented Jan 29, 2021 via email

@gvanrossum gvanrossum merged commit 5c5a938 into python:master Jan 30, 2021
@bedevere-bot
Copy link

@gvanrossum: Please replace # with GH- in the commit message next time. Thanks!

@gvanrossum gvanrossum deleted the slotscache branch January 30, 2021 02:02
adorilson pushed a commit to adorilson/cpython that referenced this pull request Mar 13, 2021
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.

7 participants