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-117139: Set up the tagged evaluation stack by Fidget-Spinner · Pull Request #117186 · python/cpython

Conversation

@Fidget-Spinner

@Fidget-Spinner Fidget-Spinner commented Mar 23, 2024 •
edited by bedevere-app Bot
Loading

Uh oh!

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

Copy link
Copy Markdown
Member

This PR is up mostly just for discussion. It compiles (with warnings) and passes (Ubuntu) tests as of now.

The main point of contention is how do we deal with bytecode that expect arrays of untagged objects? E.g. vectorcall takes an array from the stack of objects. There are two general approaches I can think of:

  1. Untag all values used by the bytecode on the stack, then let them do the usual. This has really bad performance.
  2. Secretly check for tagged values inside API. That is -- make them support tagged values internally, but don't change their signature on the outside. This way we don't break C API. This is kind of bad from a type safety standpoint (and needs us to cast things around), but it's the one with the least performance loss.

For example I currently have a _PyList_FromArraySteal and a _PyList_FromTaggedArraySteal. With the 2nd approach, I just need some bitwise operations in _PyList_FromArraySteal and it should be good to go. Then I can remove _PyList_FromTaggedArraySteal. What do y'all think?

However, we still have to untag everything if we call escaping functions that call 3rd party code (e.g. vectorcall). This is only safe right now for CPython's own C API.

  • Issue: Set up tagged pointers in the evaluation stack #117139

Sorry, something went wrong.

Uh oh!

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

corona10 reacted with eyes emoji
All reactions
  • 👀 1 reaction
Fidget-Spinner added 4 commits March 24, 2024 01:04
@Fidget-Spinner
Tag objects in ceval, type, gen, frame
7ca9b11
@Fidget-Spinner
partially convert cases
09dbeca
@Fidget-Spinner
fix the rest
84142a5
@Fidget-Spinner
fixups
25bf135
@bedevere-app bedevere-app Bot mentioned this pull request Mar 23, 2024
Set up tagged pointers in the evaluation stack #117139
Closed
Fidget-Spinner and others added 5 commits March 27, 2024 03:58
@Fidget-Spinner
fix all remaining warnings
d484628
@Fidget-Spinner
fix tests
744357e
@Fidget-Spinner
fix frames
4cc9fb8
@Fidget-Spinner
Merge remote-tracking branch 'upstream/main' into tagged_evaluation_s…
8423e75
…tack
@blurb-it
📜🤖 Added by blurb_it.
6b9ad92
@Fidget-Spinner
Fidget-Spinner marked this pull request as ready for review March 26, 2024 20:31
@Fidget-Spinner
Fidget-Spinner requested review from gvanrossum, markshannon and methane as code owners March 26, 2024 20:31
@bedevere-app bedevere-app Bot added the awaiting core review label Mar 26, 2024
@Fidget-Spinner
Fidget-Spinner requested a review from colesbury March 26, 2024 20:32
Fidget-Spinner added 4 commits March 27, 2024 04:33
@Fidget-Spinner
fix mypy errors
0bb6def
@Fidget-Spinner
Merge branch 'tagged_evaluation_stack' of github.com:Fidget-Spinner/c…
8fe65c1
…python into tagged_evaluation_stack
@Fidget-Spinner
fix mypy for real this time
8412d46
@Fidget-Spinner
fix windows builds
973c41c
@colesbury

colesbury commented Mar 26, 2024 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor

I'm a confused by your description of approach 2:

Secretly check for tagged values inside API. That is -- make them support tagged values internally, but don't change their signature on the outside.

That does not look like the approach in the PR, which adds new functions that take tagged values. The approach in the PR seems correct. We can change the signatures of internal-only APIs as needed.

However, we still have to untag everything if we call escaping functions that call 3rd party code (e.g. vectorcall)

If you untag in-place on the stack, you need to Py_INCREF() deferred references: if you mark them as non-deferred on the evaluation stack then they need to reflect that.

I think it's better to do the untagging off the evaluation stack. In the common case, you can have some fixed size PyObject *buf[N] on the C stack and untag into it, for some reasonable small fixed N. In the general case,

All reactions

Sorry, something went wrong.

Uh oh!

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

colesbury
colesbury reviewed Mar 26, 2024
View reviewed changes
Comment thread Include/object.h Outdated
Comment on lines +231 to +247
typedef union {
PyObject *obj;
uintptr_t bits;
} _Py_TaggedObject;

#define Py_OBJECT_TAG (0b0)

#ifdef Py_GIL_DISABLED
#define Py_CLEAR_TAG(tagged) ((PyObject *)((tagged).bits & ~(Py_OBJECT_TAG)))
#else
#define Py_CLEAR_TAG(tagged) ((PyObject *)(uintptr_t)((tagged).bits))
#endif

#define Py_OBJ_PACK(obj) ((_Py_TaggedObject){.bits = (uintptr_t)(obj)})

#define Py_TAG_CAST(o) ((_Py_TaggedObject){.obj = (o)})

@colesbury colesbury Mar 26, 2024

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 should not make any of these macros or data structures part of the public C API.

Sorry, something went wrong.

Uh oh!

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

Fidget-Spinner reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
@Fidget-Spinner

Fidget-Spinner commented Mar 26, 2024

Copy link
Copy Markdown
Member Author

I'm a confused by your description of approach 2:

Secretly check for tagged values inside API. That is -- make them support tagged values internally, but don't change their signature on the outside.

That does not look like the approach in the PR, which adds new functions that take tagged values. The approach in the PR seems correct for internal-only APIs -- we can change their signatures as needed.

I did a mix of both -- for internal functions I converted them to tagged form. For stuff that might be used by other people eg. _PyList_FromArraySteal, I made them internally support tagged pointers.

I think it's better to do the untagging off the evaluation stack. In the common case, you can have some fixed size PyObject *buf[N] on the C stack and untag into it, for some reasonable small fixed N. In the general case,

Ok I will think about that. That does make sense -- to pass to a scratch buffer for now. If we exceed the buffer I assume we need to untag the C stack then. One thing we might want to be wary of is overflowing the C recursion stack with the current recursion limit. Since this will make _Py_EvalEvalFrameDefault a little larger.

All reactions

Sorry, something went wrong.

Uh oh!

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

@Fidget-Spinner
fix JIT builds
4428279
@Fidget-Spinner
Fidget-Spinner requested a review from brandtbucher as a code owner March 26, 2024 21:01
@Fidget-Spinner

Fidget-Spinner commented Mar 26, 2024 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Member Author

Also to note: every Py_DECREF(Py_CLEAR_TAG(x)) and Py_INCREF(Py_CLEAR_TAG(x)) and so on is an opportunity to swap out with Py_DECREF_TAGGED(x) and so on in a future PR, when we implement the actual deferring. To keep this PR small and easier to review, I didn't do them for now. Though I think I might as well since this PR is quite bulky already, and to reduce code churn in the future.

All reactions

Sorry, something went wrong.

Uh oh!

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

@Fidget-Spinner Fidget-Spinner changed the title gh-117139: Tagged evaluation stack gh-117139: Set up the tagged evaluation stack Mar 26, 2024
@colesbury

colesbury commented Mar 26, 2024

Copy link
Copy Markdown
Contributor

For stuff that might be used by other people eg. _PyList_FromArraySteal, I made them internally support tagged pointers.

That's undefined behavior in C -- let's avoid it if possible. If we're concerned about external users, add a new function that takes tagged references (and keep the old one unused, if desired).

If we exceed the buffer I assume we need to untag the C stack then...

If we exceed the fixed size buffer we should allocate enough space for a temporary buffer using PyMem_Malloc().

One thing we might want to be wary of is overflowing the C recursion stack...

I would structure this so that the small, temporary buffer is only used when we perform a vectorcall (and not to a Python function). For example, in pseudo-code:

#define N 5 

PyObject *
PyObject_VectorcallTagged(PyObject *callable, const _Py_TaggedObject *tagged, size_t nargs, PyObject *kwnames)
{
  PyObject *args[N];
  if (nargs >= N) { 
    return PyObject_VectorcallTaggedSlow(callable, tagged, nargs, kwnames);
  }
  untag(args, tagged, nargs);
  return PyObject_Vectorcall(callable, args, nargs, kwnames);
}

PyObject *
PyObject_VectorcallTaggedSlow(PyObject *callable, const _Py_TaggedObject *tagged, size_t nargs, PyObject *kwnames)
{
  PyObject *args = PyMem_Malloc(nargs * sizeof(PyObject*));
  if (args == NULL) ...
  untag(args, tagged, nargs);
  PyObject *res = PyObject_Vectorcall(callable, args, nargs, kwnames);
  PyMem_Free(args);
  return res;
}
All reactions

Sorry, something went wrong.

Uh oh!

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

@Fidget-Spinner

Fidget-Spinner commented Mar 26, 2024

Copy link
Copy Markdown
Member Author

Thanks for the clear explanation! I will address them tomorrow.

All reactions

Sorry, something went wrong.

Uh oh!

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

@gvanrossum

gvanrossum commented Mar 27, 2024

Copy link
Copy Markdown
Member

Hm, I now recall that the problem of untagging arrays of arguments sunk my attempt (I got it to work, but it was too slow). Hope you fare better!

All reactions

Sorry, something went wrong.

Uh oh!

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

colesbury
colesbury reviewed Apr 11, 2024
View reviewed changes
Comment thread Python/gc_free_threading.c Resolved

Uh oh!

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

colesbury
colesbury reviewed Apr 11, 2024
View reviewed changes
Comment thread Python/gc_free_threading.c Resolved

Uh oh!

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

Fidget-Spinner added 4 commits April 13, 2024 02:28
@Fidget-Spinner
fix bugs in frame pushing and popping
bc2a6ec
@Fidget-Spinner
fix for shim frames
3bb3de2
@Fidget-Spinner
stackref -> tagged
8afd8b6
@Fidget-Spinner
Merge remote-tracking branch 'upstream/main' into tagged_evaluation_s…
87fda3e
…tack
colesbury
colesbury reviewed Apr 12, 2024
View reviewed changes
Comment thread Python/gc_free_threading.c Resolved

Uh oh!

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

Fidget-Spinner added 12 commits April 13, 2024 04:53
@Fidget-Spinner
Fix _PyFrame_Copy
3269f20
@Fidget-Spinner
fix memleak and address sam's comment
693b64b
@Fidget-Spinner
all tests pass except references
03af3a4
@Fidget-Spinner
Fix some refleaks
aef8e3c
@Fidget-Spinner
fix another references
87112bb
@Fidget-Spinner
clean up a little
a34cec8
@Fidget-Spinner
undo gc changes
5b4ddb6
@Fidget-Spinner
rename stack to stackref
019bc3d
@Fidget-Spinner
defer stack for bound methods
b38e507
@Fidget-Spinner
fix a bunch of ownership, and deferred methods
933b5b4
@Fidget-Spinner
Merge remote-tracking branch 'upstream/main' into tagged_evaluation_s…
11af18d
…tack
@Fidget-Spinner
fix upstream merge conflicts
1bbf021
@nineteendo

nineteendo commented Apr 25, 2024

Copy link
Copy Markdown
Contributor

Could you also configure pre-commit? https://devguide.python.org/getting-started/setup-building/#install-pre-commit

index 2bd9c40..3aa7dea 100644
--- a/Tools/cases_generator/analyzer.py
+++ b/Tools/cases_generator/analyzer.py
@@ -359,7 +359,7 @@ def has_error_without_pop(op: parser.InstDef) -> bool:
     "Py_XDECREF_STACKREF",
     "Py_INCREF_STACKREF",
     "Py_XINCREF_TAGGED",
-    "Py_NewRef_StackRef",    
+    "Py_NewRef_StackRef",
     "Py_INCREF",
     "_PyManagedDictPointer_IsValues",
     "_PyObject_GetManagedDict",
All reactions

Sorry, something went wrong.

Uh oh!

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

Fidget-Spinner added 5 commits April 25, 2024 23:58
@Fidget-Spinner
Merge remote-tracking branch 'upstream/main' into tagged_evaluation_s…
bd6889b
…tack
@Fidget-Spinner
fix merge conflicts
bafd342
@Fidget-Spinner
lint
752a49b
@Fidget-Spinner
use old code, fix decref on traceback
c6be5aa
@Fidget-Spinner
make bound methods work for keyword calls as well
d59145b
@Fidget-Spinner

Fidget-Spinner commented Apr 26, 2024

Copy link
Copy Markdown
Member Author

!buildbot nogil

All reactions

Sorry, something went wrong.

Uh oh!

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

@bedevere-bot

bedevere-bot commented Apr 26, 2024

Copy link
Copy Markdown

🤖 New build scheduled with the buildbot fleet by @Fidget-Spinner for commit d59145b 🤖

The command will test the builders whose names match following regular expression: nogil

The builders matched are:

  • x86-64 MacOS Intel ASAN NoGIL PR
  • AMD64 Ubuntu NoGIL Refleaks PR
  • ARM64 MacOS M1 NoGIL PR
  • AMD64 Windows Server 2022 NoGIL PR
  • AMD64 Ubuntu NoGIL PR
  • ARM64 MacOS M1 Refleaks NoGIL PR
  • x86-64 MacOS Intel NoGIL PR
All reactions

Sorry, something went wrong.

Uh oh!

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

@Fidget-Spinner

Fidget-Spinner commented Apr 26, 2024 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Member Author

Here are results from Sam's microbenchmark suite testing scalability. Note that I've only implemented deferred refcounting for method and function calls.

Specs:
20 virtual cores (hyper threading)
pyperf system tune

Main:
object_cfunction MEGA FAILED: 1.1x slower
cmodule_function MEGA FAILED: 2.1x slower
generator FAILED: 3.1x faster
pymethod MEGA FAILED: 1.4x slower
pyfunction MEGA FAILED: 2.7x slower
module_function MEGA FAILED: 1.9x slower
load_string_const MEGA FAILED: 2.1x slower
load_tuple_const MEGA FAILED: 1.9x slower
create_closure MEGA FAILED: 3.6x slower
create_pyobject MEGA FAILED: 2.9x slower

My branch:
object_cfunction FAILED: 9.3x faster
cmodule_function MEGA FAILED: 2.0x slower
generator FAILED: 2.9x faster
pymethod MEGA FAILED: 1.1x slower
pyfunction MEGA FAILED: 2.4x slower
module_function MEGA FAILED: 1.9x slower
load_string_const MEGA FAILED: 2.2x slower
load_tuple_const MEGA FAILED: 2.1x slower
create_closure MEGA FAILED: 3.6x slower
create_pyobject MEGA FAILED: 3.1x slower

Notice that object_cfunction went from 1.1x slower to 9.3x faster on a 20 thread workload!

All tests pass except ASAN. I am now happy with the state this PR is in. So I will close this and start upstreaming things in peices.

All reactions

Sorry, something went wrong.

Uh oh!

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

@Fidget-Spinner Fidget-Spinner closed this Apr 26, 2024
@Fidget-Spinner

Fidget-Spinner commented Apr 26, 2024 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Member Author

Benchmarks for the default build suggest a 1% speedup https://github.com/faster-cpython/benchmarking-public/blob/main/results/bm-20240426-3.13.0a6%2B-d59145b/bm-20240426-linux-x86_64-Fidget%252dSpinner-tagged_evaluation_st-3.13.0a6%2B-d59145b-vs-base.md

All reactions

Sorry, something went wrong.

Uh oh!

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

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

Reviewers

@colesbury colesbury colesbury left review comments
@gvanrossum gvanrossum Awaiting requested review from gvanrossum
@markshannon markshannon Awaiting requested review from markshannon markshannon is a code owner
@methane methane Awaiting requested review from methane methane is a code owner
@brandtbucher brandtbucher Awaiting requested review from brandtbucher brandtbucher is a code owner
@ericsnowcurrently ericsnowcurrently Awaiting requested review from ericsnowcurrently ericsnowcurrently will be requested when the pull request is marked ready for review ericsnowcurrently is a code owner
@ZeroIntensity ZeroIntensity Awaiting requested review from ZeroIntensity ZeroIntensity will be requested when the pull request is marked ready for review ZeroIntensity is a code owner
@FFY00 FFY00 Awaiting requested review from FFY00 FFY00 will be requested when the pull request is marked ready for review FFY00 is a code owner
@diegorusso diegorusso Awaiting requested review from diegorusso diegorusso will be requested when the pull request is marked ready for review diegorusso is a code owner
@brettcannon brettcannon Awaiting requested review from brettcannon brettcannon will be requested when the pull request is marked ready for review brettcannon is a code owner
@ncoghlan ncoghlan Awaiting requested review from ncoghlan ncoghlan will be requested when the pull request is marked ready for review ncoghlan is a code owner
@warsaw warsaw Awaiting requested review from warsaw warsaw will be requested when the pull request is marked ready for review warsaw is a code owner
@kumaraditya303 kumaraditya303 Awaiting requested review from kumaraditya303 kumaraditya303 will be requested when the pull request is marked ready for review kumaraditya303 is a code owner
@erlend-aasland erlend-aasland Awaiting requested review from erlend-aasland erlend-aasland will be requested when the pull request is marked ready for review erlend-aasland is a code owner
@AA-Turner AA-Turner Awaiting requested review from AA-Turner AA-Turner will be requested when the pull request is marked ready for review AA-Turner is a code owner
@emmatyping emmatyping Awaiting requested review from emmatyping emmatyping will be requested when the pull request is marked ready for review emmatyping is a code owner
@itamaro itamaro Awaiting requested review from itamaro itamaro will be requested when the pull request is marked ready for review itamaro is a code owner

Uh oh!

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

Assignees

No one assigned

Labels

None yet

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.

8 participants

@Fidget-Spinner @colesbury @gvanrossum @brandtbucher @corona10 @markshannon @nineteendo @bedevere-bot
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.