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

Small ints aren't always cached properly #90519

Closed
brandtbucher opened this issue Jan 12, 2022 · 6 comments
Closed

Small ints aren't always cached properly #90519

brandtbucher opened this issue Jan 12, 2022 · 6 comments
Assignees
Labels
3.11 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error

Comments

@brandtbucher
Copy link
Member

BPO 46361
Nosy @rhettinger, @mdickinson, @brandtbucher
PRs
  • bpo-46361: Fix "small" int caching #30583
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/brandtbucher'
    closed_at = <Date 2022-01-16.16:07:12.545>
    created_at = <Date 2022-01-12.22:50:40.310>
    labels = ['interpreter-core', 'type-bug', '3.11']
    title = "Small ints aren't always cached properly"
    updated_at = <Date 2022-01-16.16:07:12.544>
    user = 'https://github.com/brandtbucher'

    bugs.python.org fields:

    activity = <Date 2022-01-16.16:07:12.544>
    actor = 'mark.dickinson'
    assignee = 'brandtbucher'
    closed = True
    closed_date = <Date 2022-01-16.16:07:12.545>
    closer = 'mark.dickinson'
    components = ['Interpreter Core']
    creation = <Date 2022-01-12.22:50:40.310>
    creator = 'brandtbucher'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 46361
    keywords = ['patch']
    message_count = 6.0
    messages = ['410437', '410468', '410469', '410472', '410514', '410698']
    nosy_count = 3.0
    nosy_names = ['rhettinger', 'mark.dickinson', 'brandtbucher']
    pr_nums = ['30583']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue46361'
    versions = ['Python 3.11']

    @brandtbucher
    Copy link
    Member Author

    To my surprise, it seems that it's possible to create "small" integers that should live in _PyLong_SMALL_INTS, but don't. Here are two examples I've found:

    >>> import decimal
    >>> i = int(decimal.Decimal(42))  # Modules/_decimal/_decimal.c:dec_as_long
    >>> i
    42
    >>> i is 42
    <stdin>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
    False
    >>> i = int.from_bytes(bytes([42]))  # Objects/longobject.c:_PyLong_FromByteArray
    >>> i
    42
    >>> i is 42
    <stdin>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
    False

    I'm not entirely sure if this is "allowed" or not, but in any case it seems beneficial to reach into the small ints here (provided it doesn't hurt performance, of course).

    I'm testing out simple fixes for both of these now.

    @brandtbucher brandtbucher added the 3.11 only security fixes label Jan 12, 2022
    @brandtbucher brandtbucher self-assigned this Jan 12, 2022
    @brandtbucher brandtbucher added interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error 3.11 only security fixes labels Jan 12, 2022
    @brandtbucher brandtbucher self-assigned this Jan 12, 2022
    @brandtbucher brandtbucher added interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error labels Jan 12, 2022
    @mdickinson
    Copy link
    Member

    I don't *think* we currently rely on small integers being cached anywhere in the implementation (and neither do we guarantee anywhere in the docs that small integers will be cached), so as far as I can tell these omissions shouldn't lead to user-visible bugs.

    I agree that these cases should be fixed, though.

    @mdickinson
    Copy link
    Member

    Hmm. This sort of thing is a little dodgy, though (despite the comment that it's "okay"):

    if (res == zero) {

        PyObject *zero = _PyLong_GetZero();  // borrowed ref
        for (i = 1; i < nargs; i++) {
            /* --- 8< --- snipped code */
            if (res == zero) {
                /* Fast path: just check arguments.
                   It is okay to use identity comparison here. */
                Py_DECREF(x);
                continue;
            }
            /* --- 8< --- snipped code*/
        }

    @mdickinson
    Copy link
    Member

    And there are some similar things going on in rangeobject.c.

    if (r->step == _PyLong_GetOne()) {

            if (r->step == _PyLong_GetOne()) {
                return idx;
            }

    Again, technically "okay", since it's only a fast path and the slow path that follows will still do the right thing with a 1 that's not "the" 1, but it feels fragile.

    @brandtbucher
    Copy link
    Member Author

    The attached PR doesn't seem to have any impact on Decimal performance (non-optimized, non-debug build on a fairly quiet laptop):

    main:

    Convert 262,000 Decimals to "small" ints: 31.7 ms +- 5.3 ms
    Convert 256,000 Decimals to 1-digit ints: 29.9 ms +- 3.1 ms
    Convert 256,000 Decimals to 2-digit ints: 30.4 ms +- 2.8 ms
    Convert 256,000 Decimals to 3-digit ints: 31.2 ms +- 3.1 ms

    patched:

    Convert 262,000 Decimals to "small" ints: 30.9 ms +- 4.0 ms
    Convert 256,000 Decimals to 1-digit ints: 29.5 ms +- 3.0 ms
    Convert 256,000 Decimals to 1-digit ints: 30.5 ms +- 2.5 ms
    Convert 256,000 Decimals to 1-digit ints: 31.0 ms +- 2.3 ms

    @mdickinson
    Copy link
    Member

    New changeset 5cd9a16 by Brandt Bucher in branch 'main':
    bpo-46361: Fix "small" int caching (GH-30583)
    5cd9a16

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.11 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    2 participants