Skip to content

Conversation

@brandtbucher
Copy link
Member

@brandtbucher brandtbucher commented Nov 18, 2025

As mentioned in the issue, clearing these cycles explicitly results in a ~10% slowdown for json.dump microbenchmarks when the GC is disabled, but a ~20% speedup when the GC is enabled (since we're now eagerly doing the work done less efficiently by the GC).

I've also removed a "hack" that tried to turn globals into locals (in practice these "locals" are actually cells). This may have been faster in the past, but what it really does now is turn cached LOAD_GLOBAL specializations into unoptimized LOAD_DEREF instructions. Removing these gives another ~10% or so speedup for me.

@brandtbucher brandtbucher self-assigned this Nov 18, 2025
@brandtbucher brandtbucher added performance Performance or resource usage stdlib Standard Library Python modules in the Lib/ directory labels Nov 18, 2025
elif isinstance(value, int):
# see comment for int/float in _make_iterencode
yield _intstr(value)
yield int.__repr__(value)
Copy link
Member

Choose a reason for hiding this comment

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

How does this affect performance for a large list of integers?

Copy link
Member Author

Choose a reason for hiding this comment

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

Looks like doing things the "normal" way (int.__repr__) is about 0-10% faster on a little microbenchmark:

LOOPS = 1 << 24

def outer():
    _intstr = int.__repr__
    def old():
        for _ in range(LOOPS):
            _intstr(42)
    def new():
        for _ in range(LOOPS):
            int.__repr__(42)
    return old, new

def bench(func):
    start = time.perf_counter()
    func()
    end = time.perf_counter()
    print(end - start)

old, new = outer()
bench(old)
bench(new)

I think the combination of cell + bound method unwrapping is enough overhead to cancel out the gains from avoiding a (cached) global lookup and a (cached) attribute load. But it's pretty much in the noise anyways, so probably not worth keeping the old micro-optimization.

Copy link
Member

Choose a reason for hiding this comment

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

This bench shows only 1-2% difference on my machine.

Copy link
Member

Choose a reason for hiding this comment

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

Thank you. On my machine, this bench shows 2-3% difference on 3.14 and main and 20-25% difference on 3.12 and 3.13. So this micro-optimization was justified in the past, but perhaps not so much now. It cannot be backported if you are planning to backport this PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

No plans to backport.

Copy link
Member

@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

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

LGTM. 👍

@brandtbucher brandtbucher merged commit 4cfa695 into python:main Nov 18, 2025
88 of 89 checks passed
@bedevere-bot
Copy link

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot AMD64 Debian root 3.x (tier-1) has failed when building commit 4cfa695.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/345/builds/12711) and take a look at the build logs.
  4. Check if the failure is related to this commit (4cfa695) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/345/builds/12711

Failed tests:

  • test.test_os.test_os

Failed subtests:

  • test_timerfd_ns_poll - test.test_os.test_os.TimerfdTests.test_timerfd_ns_poll

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File "/root/buildarea/3.x.angelico-debian-amd64/build/Lib/test/test_os/test_os.py", line 4123, in test_timerfd_ns_poll
    self.check_timerfd_poll(True)
    ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/root/buildarea/3.x.angelico-debian-amd64/build/Lib/test/test_os/test_os.py", line 4108, in check_timerfd_poll
    self.assertEqual(self.read_count_signaled(fd), 1)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 8 != 1


Traceback (most recent call last):
  File "/root/buildarea/3.x.angelico-debian-amd64/build/Lib/test/test_os/test_os.py", line 4123, in test_timerfd_ns_poll
    self.check_timerfd_poll(True)
    ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/root/buildarea/3.x.angelico-debian-amd64/build/Lib/test/test_os/test_os.py", line 4108, in check_timerfd_poll
    self.assertEqual(self.read_count_signaled(fd), 1)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 3 != 1

michaelkiper pushed a commit to michaelkiper/cpython that referenced this pull request Nov 18, 2025
StanFromIreland pushed a commit to StanFromIreland/cpython that referenced this pull request Dec 6, 2025
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 Standard Library Python modules in the Lib/ directory

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants