GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-143732: Add tier2 specialization for TO_BOOL by eendebakpt · Pull Request #148271 · python/cpython

Conversation

@eendebakpt

@eendebakpt eendebakpt commented Apr 8, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

See discussion at #148113.

(description updated after refactoring)

This PR adds two tier2 opcodes for specialization of TO_BOOL. The *args and **kwargs arguments are marked in tier2 as tuple and dict, respectively. A recording uop is added to TO_BOOL.

Benchmark main branch Speedup
to_bool_dict_recorded 1.21 sec 852 ms 1.41×
to_bool_tuple_recorded 1.21 sec 850 ms 1.42×
to_bool_bytes_true 1.20 sec 852 ms 1.41×
to_bool_bytes_false 922 ms 566 ms 1.63×
to_bool_bytearray 1.20 sec 851 ms 1.41×
to_bool_set_recorded 1.23 sec 849 ms 1.45×
to_bool_frozenset 1.23 sec 850 ms 1.45×
to_bool_kwargs_nonempty 1.26 sec 912 ms 1.38×
to_bool_kwargs_empty 952 ms 576 ms 1.65×
to_bool_varargs_nonempty 1.23 sec 892 ms 1.38×
to_bool_varargs_empty 933 ms 574 ms 1.62×
to_bool_set_literal 26.3 ms 24.6 ms 1.07×
Geometric mean (ref) 1.43×
Details
"""TO_BOOL specialization benchmark.

Covers both the static (sym_get_type) and recorded (sym_get_probable_type)
paths through the JIT optimizer's _TO_BOOL rule.

Static path:
    - kwargs dict (via **kwargs slot priming)
    - varargs tuple (via *args slot priming)
    - set literal (via _BUILD_SET)

Recorded path (only reached via _RECORD_TOS_TYPE; bytes/bytearray/frozenset
have no static-type construction at all):
    - dict / tuple / bytes / bytearray / set / frozenset passed as parameter
"""

import pyperf

INNER_ITERS = 200


# ---- recorded-type path: value comes in as a parameter, no static type ----

def _recorded_inner(v):
    cnt = 0
    for _ in range(INNER_ITERS):
        if v:
            cnt += 1
    return cnt


def to_bool_dict_recorded(n):
    d = {"a": 1}
    for _ in range(n):
        _recorded_inner(d)


def to_bool_tuple_recorded(n):
    t = (1, 2, 3)
    for _ in range(n):
        _recorded_inner(t)


def to_bool_bytes_true(n):
    b = b"hello"
    for _ in range(n):
        _recorded_inner(b)


def to_bool_bytes_false(n):
    b = b""
    for _ in range(n):
        _recorded_inner(b)


def to_bool_bytearray(n):
    b = bytearray(b"hello")
    for _ in range(n):
        _recorded_inner(b)


def to_bool_set_recorded(n):
    s = {1, 2, 3}
    for _ in range(n):
        _recorded_inner(s)


def to_bool_frozenset(n):
    s = frozenset({1, 2, 3})
    for _ in range(n):
        _recorded_inner(s)


# ---- static-type path: kwargs / varargs slot priming ----

def _kwargs_inner(**kwargs):
    cnt = 0
    for _ in range(INNER_ITERS):
        if kwargs:
            cnt += 1
    return cnt


def _varargs_inner(*args):
    cnt = 0
    for _ in range(INNER_ITERS):
        if args:
            cnt += 1
    return cnt


def to_bool_kwargs_nonempty(n):
    for _ in range(n):
        _kwargs_inner(x=1, y=2)


def to_bool_kwargs_empty(n):
    for _ in range(n):
        _kwargs_inner()


def to_bool_varargs_nonempty(n):
    for _ in range(n):
        _varargs_inner(1, 2, 3)


def to_bool_varargs_empty(n):
    for _ in range(n):
        _varargs_inner()


# ---- static-type path: set literal via _BUILD_SET ----

def to_bool_set_literal(n):
    cnt = 0
    for _ in range(n):
        s = {1}
        if s:
            cnt += 1
    return cnt


N = 500_000

runner = pyperf.Runner()

runner.bench_func("to_bool_dict_recorded", to_bool_dict_recorded, N)
runner.bench_func("to_bool_tuple_recorded", to_bool_tuple_recorded, N)
runner.bench_func("to_bool_bytes_true", to_bool_bytes_true, N)
runner.bench_func("to_bool_bytes_false", to_bool_bytes_false, N)
runner.bench_func("to_bool_bytearray", to_bool_bytearray, N)
runner.bench_func("to_bool_set_recorded", to_bool_set_recorded, N)
runner.bench_func("to_bool_frozenset", to_bool_frozenset, N)
runner.bench_func("to_bool_kwargs_nonempty", to_bool_kwargs_nonempty, N)
runner.bench_func("to_bool_kwargs_empty", to_bool_kwargs_empty, N)
runner.bench_func("to_bool_varargs_nonempty", to_bool_varargs_nonempty, N)
runner.bench_func("to_bool_varargs_empty", to_bool_varargs_empty, N)
runner.bench_func("to_bool_set_literal", to_bool_set_literal, N)


  • Issue: Broader specialization in the Specializing Adaptive Interpreter for better JIT performance #143732

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
eendebakpt added 3 commits April 8, 2026 21:01
@eendebakpt
Add specializations for TO_BOOL
d35071a
@eendebakpt
type information for kwargs
44f5c49
@eendebakpt
add tests
b80a79b
@eendebakpt
eendebakpt requested review from Fidget-Spinner, markshannon, savannahostrowski and tomasr8 as code owners April 8, 2026 21:39
@bedevere-app bedevere-app Bot added the awaiting review label Apr 8, 2026
@eendebakpt
eendebakpt marked this pull request as draft April 8, 2026 21:40
@bedevere-app bedevere-app Bot removed the awaiting review label Apr 8, 2026
@bedevere-app bedevere-app Bot mentioned this pull request Apr 8, 2026
Broader specialization in the Specializing Adaptive Interpreter for better JIT performance #143732
Open
@eendebakpt
Merge branch 'main' into to_bool_specialization
ba88f27
@eendebakpt
eendebakpt marked this pull request as ready for review April 8, 2026 22:21
@bedevere-app bedevere-app Bot added the awaiting review label Apr 8, 2026
Sacul0457
Sacul0457 reviewed Apr 8, 2026
View reviewed changes
Comment thread Python/optimizer_bytecodes.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

@markshannon markshannon added the skip news label Apr 9, 2026
eendebakpt added 5 commits April 10, 2026 23:43
@eendebakpt
review comments
29311d8
@eendebakpt
Merge branch 'main' into to_bool_specialization
e9c1e24
@eendebakpt
fix merge conflict
9de1c2c
@eendebakpt
Merge branch 'main' into to_bool_specialization
91334af
@eendebakpt
Merge branch 'main' into to_bool_specialization
08a4535
kumaraditya303
kumaraditya303 reviewed Apr 14, 2026
View reviewed changes
Comment thread Python/bytecodes.c Outdated
_REPLACE_WITH_TRUE +
POP_TOP;

tier2 op(_TO_BOOL_DICT, (value -- res)) {

@kumaraditya303 kumaraditya303 Apr 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

You can merge this with _TO_BOOL_SIZED by using the fact that both do a fixed offset lookup.
In tier2 optimizer you can set the offset for where the size is stored and do size = (Py_ssize_t)((char *)obj + offset) and check that directly.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@eendebakpt eendebakpt Apr 14, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I see what you mean. It goes into the internals of PyDict (e.g. not using PyDict_GET_SIZE, but doing manual offset calculations) and we also need to store the offset somewhere. So I think this is too much of a complication to get rid of a tier2 opcode.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@kumaraditya303 kumaraditya303 Apr 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

we also need to store the offset somewhere.

You can store it in the instruction operand0.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
kumaraditya303
kumaraditya303 reviewed Apr 14, 2026
View reviewed changes
Comment thread Python/optimizer_bytecodes.c Outdated
REPLACE_OP(this_instr, _TO_BOOL_DICT, 0, 0);
}
else if (tp == &PyTuple_Type ||
tp == &PySet_Type ||

@kumaraditya303 kumaraditya303 Apr 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

This is incorrect for set as it does not uses PyObject_VAR_HEAD, this works by accident because it has fill at that offset which is incorrect if set has dummy entries.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@eendebakpt eendebakpt Apr 14, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Good catch! I updated the PR to handle the set/frozenset separately.

We can also use your suggestion to fold everything into the _TO_BOOL_SIZED. That means we have to load the offset at runtime (minor cost), but it does keep the number of ops lower. I implemented this in main...eendebakpt:to_bool_specialization_v2.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@kumaraditya303 kumaraditya303 Apr 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

That means we have to load the offset at runtime (minor cost), but it does keep the number of ops lower.

I don't think so, in the JIT the offset would be burned into the machine code itself so the offset is fixed and not looked up at runtime.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

eendebakpt reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
eendebakpt added 3 commits April 14, 2026 11:34
@eendebakpt
add regression test for set
62fae72
@eendebakpt
review comments
52f7cc6
@eendebakpt
handle set case
05c6089
kumaraditya303
kumaraditya303 reviewed Apr 14, 2026
View reviewed changes
Comment thread Python/optimizer_bytecodes.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

markshannon
markshannon reviewed Apr 14, 2026
View reviewed changes

@markshannon markshannon left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

The problem with recording uops not being allowed after specializing uops has been fixed, so you can add a recording uop to _TO_BOOL and use the recorded information for better specialization.
#148285

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Python/optimizer_bytecodes.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

@eendebakpt
remove dead code
d655709
@eendebakpt

eendebakpt commented Apr 14, 2026

Copy link
Copy Markdown
Contributor Author

The problem with recording uops not being allowed after specializing uops has been fixed, so you can add a recording uop to _TO_BOOL and use the recorded information for better specialization. #148285

@markshannon Adding the recording uop will help to specialize for several cases. But it will slow down (a tiny bit) the unspecialized cases. Should we make the recording uop change in a followup PR?

And do you have an opinion on whether to use the _TO_BOOL_DICT in this PR, or move everything to _TO_BOOL_SIZED as done in main...eendebakpt:to_bool_specialization_v2?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@eendebakpt
regenerate
0606e89
@eendebakpt
eendebakpt requested a review from markshannon April 22, 2026 21:52
eendebakpt added 2 commits April 30, 2026 22:34
@eendebakpt
Merge branch 'to_bool_specialization_v2' into to_bool_specialization
578a6e0
Take v2's parameterized _TO_BOOL_SIZED design (with size_offset operand)
which subsumes _TO_BOOL_DICT and _TO_BOOL_ANY_SET. Sets/frozensets use
PySetObject.used (not Py_SIZE, which aliases the dummy-counting fill slot).
Regenerated all generated files from the merged source.
@eendebakpt
Merge remote-tracking branch 'origin/main' into to_bool_specialization
1e01851
# Conflicts:
#	Include/internal/pycore_uop_ids.h
#	Lib/test/test_capi/test_opt.py
#	Python/optimizer_bytecodes.c
#	Python/optimizer_cases.c.h
#	Python/record_functions.c.h
@eendebakpt eendebakpt mentioned this pull request Apr 30, 2026
gh-143732: Add specialization for TO_BOOL #148113
Closed
eendebakpt and others added 6 commits April 30, 2026 23:41
@eendebakpt
fix merge conflicts and add recording uop
bb35e19
@eendebakpt
cleanup
f9a8bc5
@eendebakpt
Merge remote-tracking branch 'origin/main' into to_bool_specializatio…
ee0325c
…n_v3
@eendebakpt @claude
Restore test_opt.py from origin/main, re-applying TO_BOOL tests
c7daa03
Earlier merge resolutions silently dropped a sizable set of tests that
exist on origin/main (test_get_iter_list, test_get_iter_gen,
test_get_iter_trad, test_cached_load_special, all
test_binary_op_extend_*, test_contains_op_frozendict_const_fold,
test_not_contains_op_frozendict_const_fold,
test_load_attr_getattribute_frame, etc.). Restore the file from
origin/main and re-insert the eight TO_BOOL tests that are unique to
this branch (test_to_bool_kwargs_dict, _empty_dict, _varargs_tuple,
_empty_tuple, _args_and_kwargs, _args_kwargs_with_regular_params,
_kwargs_only_no_varargs, _set_with_dummy_entries) right after
test_to_bool_always_true.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@eendebakpt
add more tests, remove defensive code
e9a1e9a
@eendebakpt
news entry
09c07a3
@eendebakpt
eendebakpt requested review from AA-Turner, ZeroIntensity, brandtbucher, diegorusso and gaogaotiantian as code owners May 1, 2026 09:36
@eendebakpt

eendebakpt commented May 1, 2026

Copy link
Copy Markdown
Contributor Author

I decided to go with the _TO_BOOL_SIZED variation. It is only a single opcode covering many types. The PR now does 3 things: add new opcodes, add a recording uop, add type information for *args/**kwargs. If needed we can split it into multiple PRs.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

eendebakpt added 4 commits May 1, 2026 11:38
@eendebakpt
Merge remote-tracking branch 'origin/main' into to_bool_specialization
c64d69d
@eendebakpt
Merge branch 'main' into to_bool_specialization
06247eb
@eendebakpt
Merge branch 'main' into to_bool_specialization
a30ca62
@eendebakpt
Merge branch 'main' into to_bool_specialization
fa17d1d
@eendebakpt eendebakpt mentioned this pull request May 3, 2026
JIT can trigger assert(uop_buffer_remaining_space(trace) > space_needed); for widened uops #149335
Closed
kumaraditya303
kumaraditya303 reviewed May 18, 2026
View reviewed changes
Comment thread Python/optimizer_bytecodes.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

eendebakpt added 3 commits May 18, 2026 09:34
@eendebakpt
Merge branch 'main' into pr-148271
cd540b8
# Conflicts:
#	Include/internal/pycore_uop_ids.h
@eendebakpt
add frozendict
729ecc9
@eendebakpt
Merge branch 'main' into to_bool_specialization
5d3090a
kumaraditya303
kumaraditya303 reviewed May 22, 2026
View reviewed changes
Comment thread Lib/test/test_capi/test_opt.py Outdated
uops = get_opnames(ex)
self.assertNotIn("_REPLACE_WITH_TRUE", uops)

def test_to_bool_kwargs_dict(self):

@kumaraditya303 kumaraditya303 May 22, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

These tests are too verbose, I would prefer if you combine several of them into one, not each case needs a different test.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@eendebakpt eendebakpt May 22, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Done!

I also looked into refactoring the optimizer code to use a table based approach. The result is

e121f12

If you want me to pull it into the branch give me a thumbs up.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@eendebakpt
refactor tests
43c54d4
@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown

This PR is stale because it has been open for 90 days with no activity.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@github-actions github-actions Bot added the stale Stale PR or inactive for long period of time. label Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@kumaraditya303 kumaraditya303 kumaraditya303 left review comments
@tomasr8 tomasr8 Awaiting requested review from tomasr8 tomasr8 is a code owner
@Fidget-Spinner Fidget-Spinner Awaiting requested review from Fidget-Spinner Fidget-Spinner is a code owner
@savannahostrowski savannahostrowski Awaiting requested review from savannahostrowski
@markshannon markshannon Awaiting requested review from markshannon markshannon is a code owner
@gaogaotiantian gaogaotiantian Awaiting requested review from gaogaotiantian
@brandtbucher brandtbucher Awaiting requested review from brandtbucher
@diegorusso diegorusso Awaiting requested review from diegorusso
@ZeroIntensity ZeroIntensity Awaiting requested review from ZeroIntensity
@AA-Turner AA-Turner Awaiting requested review from AA-Turner
+1 more reviewer
@Sacul0457 Sacul0457 Sacul0457 left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

awaiting review skip news stale Stale PR or inactive for long period of time.

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@eendebakpt @markshannon @kumaraditya303 @Sacul0457
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.