Skip to content

gh-82088: Improve performance of PyLong_As*() for multi-digit ints#135585

Merged
vstinner merged 16 commits intopython:mainfrom
eendebakpt:pylong_aslong
Jul 10, 2025
Merged

gh-82088: Improve performance of PyLong_As*() for multi-digit ints#135585
vstinner merged 16 commits intopython:mainfrom
eendebakpt:pylong_aslong

Conversation

@eendebakpt
Copy link
Copy Markdown
Contributor

@eendebakpt eendebakpt commented Jun 16, 2025

This is a continuation of the work in #15363 by @sir-sigurd.

Performance is improved by

  • Unrolling the part of the loop where no overflow can occur
  • Improving the overflow check so no temporary variable is used

Performance: main

pack n dt 460.21 [ms]
pack N dt 476.32 [ms]
pack l dt 479.76 [ms]
pack L dt 451.84 [ms]

PR

pack n dt 401.02 [ms]
pack N dt 391.06 [ms]
pack l dt 408.83 [ms]
pack L dt 402.37 [ms]

Script:

import time
from struct import Struct

if __name__ == "__main__":
    for key in 'nNlL':
        N = 1000
        pack = Struct(key*N).pack
        values = tuple(range(2**30, 2**30 + N))
            
        t0=time.perf_counter()
        for _ in range(50_000):
            pack(*values);
        dt=time.perf_counter()-t0
        
        print(f'pack {key} dt {dt*1e3:.2f} [ms]')

(performance gain is similar to earlier reported improvements)

Comment thread Objects/longobject.c Outdated
assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1));
x = digits[i];

#if ((ULONG_MAX >> PyLong_SHIFT)) >= ((1UL << PyLong_SHIFT) - 1)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We can also unroll the second digit for the other methods. It gains a bit of performance at the cost of a bit of extra code. The unrolling can be factored out to a helper function (the loop itself is not so easy because there are small differences in how to handle the overflow)

Comment thread Objects/longobject.c Outdated
Comment thread Objects/longobject.c Outdated
eendebakpt and others added 2 commits June 17, 2025 16:46
Co-authored-by: Victor Stinner <vstinner@python.org>
@eendebakpt eendebakpt changed the title Draft: gh-82088: Improve performance of PyLong_As*() for multi-digit ints gh-82088: Improve performance of PyLong_As*() for multi-digit ints Jun 17, 2025
@eendebakpt eendebakpt requested a review from vstinner June 24, 2025 11:26
Comment thread Objects/longobject.c Outdated
@vstinner
Copy link
Copy Markdown
Member

I rewrote the benchmark using pyperf:

import pyperf
from struct import Struct

N = 1000

runner = pyperf.Runner()
values = tuple(range(2**30, 2**30 + N))
for key in 'nNlL':
    pack = Struct(key*N).pack
    runner.bench_func(f'pack {key}', pack, *values)

Result:

Benchmark ref change
pack n 7.86 us 5.80 us: 1.35x faster
pack N 7.06 us 5.77 us: 1.22x faster
pack l 9.00 us 6.31 us: 1.43x faster
pack L 7.66 us 6.00 us: 1.28x faster
Geometric mean (ref) 1.32x faster

Comment thread Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst Outdated
Comment thread Objects/longobject.c Outdated
Comment thread Objects/longobject.c Outdated
Comment thread Objects/longobject.c Outdated
Comment thread Objects/longobject.c Outdated
Comment thread Objects/longobject.c
Comment thread Objects/longobject.c
Copy link
Copy Markdown
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

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

LGTM. I have two last minor suggestions.

@serhiy-storchaka: Would you mind to double check that the integer overflow checks are correct?

Replace:

            prev = x;
            x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
            if ((x >> PyLong_SHIFT) != prev) ...

with:

            if (x > (ULONG_MAX >> PyLong_SHIFT)) ...
            x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];

Comment thread Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst Outdated
Comment thread Objects/longobject.c Outdated
Co-authored-by: Victor Stinner <vstinner@python.org>
Comment thread Objects/longobject.c
@vstinner vstinner merged commit d754f75 into python:main Jul 10, 2025
40 checks passed
@vstinner
Copy link
Copy Markdown
Member

Thanks @eendebakpt, I merged your PR. Let's see how it goes :-)

AndPuQing pushed a commit to AndPuQing/cpython that referenced this pull request Jul 11, 2025
…nts (python#135585)

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
Pranjal095 pushed a commit to Pranjal095/cpython that referenced this pull request Jul 12, 2025
…nts (python#135585)

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
picnixz pushed a commit to picnixz/cpython that referenced this pull request Jul 13, 2025
…nts (python#135585)

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
taegyunkim pushed a commit to taegyunkim/cpython that referenced this pull request Aug 4, 2025
…nts (python#135585)

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
Agent-Hellboy pushed a commit to Agent-Hellboy/cpython that referenced this pull request Aug 19, 2025
…nts (python#135585)

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
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.

3 participants