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-100227: Move the Dict of Interned Strings to PyInterpreterState by ericsnowcurrently · Pull Request #102339 · python/cpython

Conversation

@ericsnowcurrently

@ericsnowcurrently ericsnowcurrently commented Feb 28, 2023 •
edited by bedevere-bot
Loading

Uh oh!

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

Copy link
Copy Markdown
Member
  • Issue: Improve Interpreter Isolation #100227

Sorry, something went wrong.

Uh oh!

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

All reactions
ericsnowcurrently added 3 commits February 28, 2023 14:18
@ericsnowcurrently
set_interned_dict() -> ensure_interned_dict().
fa1d9a0
@ericsnowcurrently
Move the interned dict to PyInterpreterState.
23e2211
@ericsnowcurrently
Intern the static strings for every interpreter.
969caba
@ericsnowcurrently ericsnowcurrently added the skip news label Feb 28, 2023
@bedevere-bot bedevere-bot added the awaiting core review label Feb 28, 2023
@bedevere-bot bedevere-bot mentioned this pull request Feb 28, 2023
Improve Interpreter Isolation #100227
Closed
kumaraditya303
kumaraditya303 requested changes Mar 3, 2023
View reviewed changes

@kumaraditya303 kumaraditya303 left a comment

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.

IMO this is wrong, it breaks the invariant that interned strings are global, with this they are per interpreter. When a string is interned, its interned bit is set to 1 and if it is shared across interpreters like if it's immortal then it breaks the invariant that interned bit set means that the string is really interned.

The correct solution for this is to let the interned dict be global and add a fine grained lock around mutating it which IIRC only happens in two places.

Sorry, something went wrong.

Uh oh!

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

All reactions
@bedevere-bot bedevere-bot added awaiting changes and removed awaiting core review labels Mar 3, 2023
@bedevere-bot

bedevere-bot commented Mar 3, 2023

Copy link
Copy Markdown

When you're done making the requested changes, leave the comment: I have made the requested changes; please review again.

All reactions

Sorry, something went wrong.

Uh oh!

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

@ericsnowcurrently

ericsnowcurrently commented Mar 3, 2023

Copy link
Copy Markdown
Member Author

Thanks for the feedback. I want to be sure we're in agreement before I proceed on this.

IMO this is wrong, it breaks the invariant that interned strings are global, with this they are per interpreter.

Are you talking about process-global or something else? Process-global is problematic for interpreter isolation, especially under a per-interpreter GIL. Non-immortal str objects must not be shared between interpreters, which means many strings that are currently interned must either be made immortal or not be interned. Making the interned dict per-interpreter solves this.

When a string is interned, its interned bit is set to 1 and if it is shared across interpreters like if it's immortal then it breaks the invariant that interned bit set means that the string is really interned.

All immortal strings are still interned in each interpreter. However, non-immortal string instances are necessarily unique to a single interpreter and consequently interning them to just that interpreter is still correct.

The trade-off here is that we incur an extra CPU cost to startup for subinterpreters (due to the static strings) and an extra memory/CPU cost when the same (non-static) string is interned in additional interpreters. I expect there is a more efficient way to solve this, but that doesn't mean we can't go with a less efficient approach (as I've proposed) in the meantime.

Note that, most importantly, there is no impact to the status quo for the main interpreter in either case. That matters since the vast majority of Python usage does not involve subinterpreters currently.

The correct solution for this is to let the interned dict be global and add a fine grained lock around mutating it which IIRC only happens in two places.

Such a lock must also guard reads since they might race with mutation. That leads to a larger performance impact.

All reactions

Sorry, something went wrong.

Uh oh!

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

@kumaraditya303

kumaraditya303 commented Mar 7, 2023

Copy link
Copy Markdown
Contributor

I'll see it by next weekend.

ericsnowcurrently reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

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

@ericsnowcurrently

ericsnowcurrently commented Mar 8, 2023

Copy link
Copy Markdown
Member Author

BTW, benchmarks show no significant change in performance.

All reactions

Sorry, something went wrong.

Uh oh!

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

@kumaraditya303

kumaraditya303 commented Mar 11, 2023

Copy link
Copy Markdown
Contributor

Are you talking about process-global or something else? Process-global is problematic for interpreter isolation, especially under a per-interpreter GIL. Non-immortal str objects must not be shared between interpreters, which means many strings that are currently interned must either be made immortal or not be interned. Making the interned dict per-interpreter solves this.

Hmm, I am thinking of the opposite, make it mandatory that all interned strings are immortal. If we guarantee this then we don't need to make it per interpreter, the main interpreter will clear the interned table at runtime finalization. This has other benefits that it fits out current mental model that an interned string is a process wide global unique string and shared across interpreter as it is currently done.

Note that, most importantly, there is no impact to the status quo for the main interpreter in either case. That matters since the vast majority of Python usage does not involve subinterpreters currently.

Yeah, all of this is for subinterpreters.

This is how with immortal objects, I think this should happen:

  • At runtime startup all static immortal strings are added to the global interned dict. This can be done without holding lock as only one interp exists so no performance loss.
  • PyUnicode_InternInPlace is called, it immortalizes the string and adds it to the interned dict while holding a fine grained lock. Since this string is immortalized, it can be shared across interpreters without any book-keeping. This can be done from any interpreter as the dict mutation is protected via a critical section aka lock.
  • At runtime finalization, after all interpreters are destroyed, the main interpreter clears the interned dict and frees all the interned strings.

To me this seems much simpler to understand and implement. I feel we should table this for now until immortal objects are implemented in main branch.

All reactions

Sorry, something went wrong.

Uh oh!

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

@ericsnowcurrently

ericsnowcurrently commented Mar 13, 2023

Copy link
Copy Markdown
Member Author

make it mandatory that all interned strings are immortal.

I see what you're saying. Furthermore, this wouldn't be so big a deal since you can't [normally] intern a string directly from Python code, so it isn't a huge number of strings that get interned. I'll let this stew in my brain for a day or two and get back to this. Thanks for explaining!

All reactions

Sorry, something went wrong.

Uh oh!

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

@ericsnowcurrently

ericsnowcurrently commented Mar 13, 2023

Copy link
Copy Markdown
Member Author

make it mandatory that all interned strings are immortal.

This does mean that, under a per-interpreter GIL, we'd need a granular global for any use of the interned dict, but that's okay. It also means that we'd have to ensure resizing is done with the main interpreter's obmalloc state, but that is manageable.

All reactions

Sorry, something went wrong.

Uh oh!

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

@ericsnowcurrently ericsnowcurrently mentioned this pull request Mar 13, 2023
gh-101659: Isolate "obmalloc" State to Each Interpreter #101660
Merged
@ericsnowcurrently
ericsnowcurrently marked this pull request as draft March 20, 2023 23:10
@ericsnowcurrently ericsnowcurrently mentioned this pull request Mar 20, 2023
gh-100227: Make the Global Interned Dict Safe for Isolated Interpreters #102858
Closed
@ericsnowcurrently

ericsnowcurrently commented Mar 20, 2023

Copy link
Copy Markdown
Member Author

@kumaraditya303, does gh-102858 seem like what you had in mind?

All reactions

Sorry, something went wrong.

Uh oh!

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

@ericsnowcurrently ericsnowcurrently mentioned this pull request Mar 22, 2023
gh-100227: Make the Global Interned Dict Safe for Isolated Interpreters #102925
Merged
@ericsnowcurrently

ericsnowcurrently commented Mar 22, 2023

Copy link
Copy Markdown
Member Author

I'm closing this in favor of gh-102925, which keeps the interned strings dict global.

All reactions

Sorry, something went wrong.

Uh oh!

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

@ericsnowcurrently ericsnowcurrently closed this Mar 22, 2023
ericsnowcurrently added 4 commits March 27, 2023 16:23
@ericsnowcurrently
Revert "pythongh-100227: Make the Global Interned Dict Safe for Isola…
2e8773b
…ted Interpreters (pythongh-102925)"

This reverts commit 87be8d9.
@ericsnowcurrently
Fix unicode_is_finalizing().
408ee56
@ericsnowcurrently
Merge branch 'revert-global-interned-fixes' into isolate-interned-dict
4d4fdb1
@ericsnowcurrently
Merge branch 'main' into isolate-interned-dict
e53ac85
@ericsnowcurrently ericsnowcurrently reopened this Mar 27, 2023
@ericsnowcurrently

ericsnowcurrently commented Mar 27, 2023

Copy link
Copy Markdown
Member Author

The keep-it-global approach has turned out to be quite complex. We can look into that further, but in the meantime I'm planning on going with the simpler approach here.

All reactions

Sorry, something went wrong.

Uh oh!

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

@ericsnowcurrently
ericsnowcurrently marked this pull request as ready for review March 28, 2023 00:31
@ericsnowcurrently
ericsnowcurrently merged commit ba65a06 into python:main Mar 28, 2023
@bedevere-bot bedevere-bot removed the awaiting changes label Mar 28, 2023
@ericsnowcurrently
ericsnowcurrently deleted the isolate-interned-dict branch March 28, 2023 18:52
warsaw pushed a commit to warsaw/cpython that referenced this pull request Apr 11, 2023
@ericsnowcurrently @warsaw
pythongh-100227: Move the Dict of Interned Strings to PyInterpreterSt…
94a255e
…ate (pythongh-102339)

We can revisit the options for keeping it global later, if desired.  For now the approach seems quite complex, so we've gone with the simpler isolation solution in the meantime.

python#100227
@Yhg1s Yhg1s mentioned this pull request Oct 1, 2024
[3.12] gh-124785: Revert "gh-116510: Fix crash due to shared immortal interned strings (gh-124646)" (gh-124807) #124818
Closed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@kumaraditya303 kumaraditya303 kumaraditya303 requested changes

Uh oh!

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

Assignees

No one assigned

Labels

skip news

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.

3 participants

@ericsnowcurrently @bedevere-bot @kumaraditya303
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.