Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

GH-106008: Make implicit boolean conversions explicit#106003

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
brandtbucher merged 18 commits intopython:mainfrombrandtbucher:to-bool
Jun 29, 2023

Conversation

brandtbucher
Copy link
Member

@brandtbucherbrandtbucher commentedJun 23, 2023
edited by bedevere-bot
Loading

...and specialize them!

This adds a newTO_BOOL bytecode that prefixes allUNARY_NOT,POP_JUMP_IF_TRUE, andPOP_JUMP_IF_FALSE instructions, which now require an exact boolean. We also use a spare bit inCOMPARE_OP's oparg to indicate whether the result should be converted to bool (this saves aTO_BOOL for most branches, and is a no-op for allCOMPARE_OP specializations).

"0% faster". Stats show a93.5% hit rate for the new instructions.


📚 Documentation preview 📚:https://cpython-previews--106003.org.readthedocs.build/

@brandtbucherbrandtbucher added performancePerformance or resource usage interpreter-core(Objects, Python, Grammar, and Parser dirs) labelsJun 23, 2023
@brandtbucherbrandtbucher self-assigned thisJun 23, 2023
@brandtbucherbrandtbucher changed the titleMake implicit boolean conversions explicitGH-106008: Make implicit boolean conversions explicitJun 23, 2023
@brandtbucherbrandtbucher marked this pull request as ready for reviewJune 23, 2023 03:53
Copy link
Member

@markshannonmarkshannon left a comment

Choose a reason for hiding this comment

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

A few minor comments, otherwise LGTM.

}

inst(TO_BOOL_BOOL, (unused/1, unused/2, value -- value)) {
// Coolest (and dumbest-named) specialization ever:
Copy link
Member

Choose a reason for hiding this comment

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

True, but the not the most useful comment for someone trying to understand the code 🙂

brandtbucher and artugro reacted with thumbs up emoji

inst(TO_BOOL_NONE, (unused/1, unused/2, value -- res)) {
// This one is a bit weird, because we expect *some* failures...
// it might be worth combining with TO_BOOL_ALWAYS_TRUE somehow:
Copy link
Member

Choose a reason for hiding this comment

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

I think we decided it wasn't, as it reflects the underlying type instability when doingif x: as a stand in forif x is None.

brandtbucher reacted with thumbs up emoji
inst(TO_BOOL_STR, (unused/1, unused/2, value -- res)) {
DEOPT_IF(!PyUnicode_CheckExact(value), TO_BOOL);
STAT_INC(TO_BOOL, hit);
if (Py_Is(value, &_Py_STR(empty))) {
Copy link
Member

Choose a reason for hiding this comment

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

Usevalue == &_Py_STR(empty). The semantics isvalue == "", notvalue is "".
In general I wouldn't usePy_Is except when you want the exact semantics of Python'sx is y.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Hm, I mean, weare checking for the identity of the singleton string here. I'll just change it, though.

Copy link
Member

@markshannonmarkshannonJun 26, 2023
edited
Loading

Choose a reason for hiding this comment

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

Hypothetically we could have more than one"" object. I don't think that" ".strip() is "" is part of the language spec, soPy_Is doesn't add any safety, just obfuscation.

We can also potentially have tagged ints, in which casePy_Is would need to become a lot more complex, butvalue == &_Py_STR(empty) would remain efficient.


inst(TO_BOOL_ALWAYS_TRUE, (unused/1, version/2, value -- res)) {
// This one is a bit weird, because we expect *some* failures...
// it might be worth combining with TO_BOOL_NONE somehow:
Copy link
Member

Choose a reason for hiding this comment

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

See comment above.

brandtbucher reacted with thumbs up emoji
}
}
assert(PyBool_Check(cond));
JUMPBY(oparg * Py_IsFalse(cond));
Copy link
Member

Choose a reason for hiding this comment

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

This is so much more pleasing 🙂

brandtbucher reacted with thumbs up emoji
@@ -107,6 +107,8 @@ _Py_GetSpecializationStats(void) {
err += add_stat_dict(stats, COMPARE_OP, "compare_op");
err += add_stat_dict(stats, UNPACK_SEQUENCE, "unpack_sequence");
err += add_stat_dict(stats, FOR_ITER, "for_iter");
err += add_stat_dict(stats, TO_BOOL, "to_bool");
err += add_stat_dict(stats, SEND, "send");
Copy link
Member

Choose a reason for hiding this comment

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

Thanks, I tend to forget about the stats dict .

brandtbucher reacted with thumbs up emoji
@markshannon
Copy link
Member

Note for possible future PR:

We could, at the cost of two bits intp_flags avoid the version number and combine the ALWAYS_TRUE and NONE specializations.
We need aALWAYS_TRUE_OR_FALSE and aIS_TRUE bit.
Classes that don't override__bool__ or__len__ andNone would set theALWAYS_TRUE_OR_FALSE bit. TheIS_TRUE bit would be set to 0 forNone, and to 1 for always true objects.

Rather than check the version number, check theALWAYS_TRUE_OR_FALSE, thenres = (tp_flags & IS_TRUE) ? Py_True : Py_False;

For abi4, we could add a per-object bit to handle immutable objects like ints and strings.

}
}
Py_DECREF(newconst);
return index;
Copy link
Member

Choose a reason for hiding this comment

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

windows compiler warning here, implicit cast from Py_ssize_t to int

brandtbucher reacted with thumbs up emoji
@brandtbucher
Copy link
MemberAuthor

Merging is currently blocked on#106250.

@gvanrossum
Copy link
Member

Hey@brandtbucher, I have a question about this PR. In#106393 I had to change the code inPOP_JUMP_IF_TRUE/FALSE from

JUMPBY(oparg * Py_IsFalse(cond));

to

if (Py_IsFalse(cond)) {    JUMP_POP_DISPATCH(oparg, 1);  // Macro that wraps JUMPBY()}

The reason is that the uop executor currently exits whenever it jumps, and your original code from this PRalways jumps.

Did you (or@markshannon) have a reason to prefer theoparg * Py_IsFalse(cond) version over the conditional?

If there's no deep reason I'll keep it the way I coded it up; but if there is (maybe it came out faster in a micro-benchmark?) then I suppose I could fix it another way in the uop interpreter (e.g. only exiting if the jump offset is nonzero).

@markshannon
Copy link
Member

That the implementation of branches is itself branchless has a certain aesthetic appeal 🙂. TBH, that's the main reason.

The multiplication form will be quicker for unpredictable branches, and slower for predictable ones in the tier 1 interpreter.
I doubt it makes any difference in terms of overall performance, and will likely be irrelevant once the tier 2 interpreter does most of the work.

gvanrossum reacted with thumbs up emoji

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@carljmcarljmcarljm left review comments

@TeamSpen210TeamSpen210TeamSpen210 left review comments

@markshannonmarkshannonmarkshannon left review comments

@ericsnowcurrentlyericsnowcurrentlyAwaiting requested review from ericsnowcurrentlyericsnowcurrently is a code owner

@ncoghlanncoghlanAwaiting requested review from ncoghlanncoghlan is a code owner

@warsawwarsawAwaiting requested review from warsawwarsaw is a code owner

@iritkatrieliritkatrielAwaiting requested review from iritkatrieliritkatriel is a code owner

Assignees

@brandtbucherbrandtbucher

Labels
interpreter-core(Objects, Python, Grammar, and Parser dirs)performancePerformance or resource usage
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

6 participants
@brandtbucher@markshannon@gvanrossum@carljm@TeamSpen210@bedevere-bot

[8]ページ先頭

©2009-2025 Movatter.jp