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-127750: Fix singledispatchmethod caching#127839

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

Closed

Conversation

@eendebakpt
Copy link
Contributor

@eendebakpteendebakpt commentedDec 11, 2024
edited
Loading

We address two issues:

  • Thesingledispatchmethod would work incorrectly with objects that compare equal (but are not identical)
  • Thesingledispatchmethod cache would hold on to objects forever

See#127751

@eendebakpteendebakpt changed the titleDraft: gh-127750: Fix singledispatchmethod cachinggh-127750: Fix singledispatchmethod cachingDec 11, 2024
@rhettingerrhettinger removed their request for reviewDecember 12, 2024 04:23
@vodik
Copy link

I tried this locally and added and it seems to be working correctly. Haven't benchmarked it either though.

The cached methods seem to stick around in the cache until the gc fires. I haven't been able to find a way to make them stick around.

@AlexWaygoodAlexWaygood self-requested a reviewDecember 12, 2024 07:44
@dg-pb
Copy link
Contributor

Does CPython re-useid values after instances are destroyed?

SO says that yes, but it was in 2018. Is it still true?

If so, then can identity be reliably cached at all?

Maybe similar approach tocached_property could be used here? Although it would restrict to classes that haveinstance.__dict__.

@eendebakpt
Copy link
ContributorAuthor

Does CPython re-useid values after instances are destroyed?

SO says that yes, but it was in 2018. Is it still true?

If so, then can identity be reliably cached at all?

Maybe similar approach tocached_property could be used here? Although it would restrict to classes that haveinstance.__dict__.

Yes, cpython re-uses the id (the id is equal to the memory address, and memory is reused). But the id is unique during the lifetime of an object, seehttps://docs.python.org/3/library/functions.html#id.

The_method in theWeakValueDictionary has a reference toobj, so the keyid(obj)is valid and unique as long as_method exists. Once the garbage collector detects a cycle containing the_method it will remove it from the dictionary. But sinceobj is part of this cycle, it also meansobj no longer exist andid(obj) is free to re-use.

But your remark got me thinking: the PR right now is reliable, but maybe not very efficient. Runninggc.collect clears the entire cache. The benchmarks I executed showed an improvement, but that might very well be because the garbage collector is not running often.

@dg-pb
Copy link
Contributor

Runninggc.collect clears the entire cache.

Could you clarify what do you mean by this?

@eendebakpt
Copy link
ContributorAuthor

Runninggc.collect clears the entire cache.

Could you clarify what do you mean by this?
Take for example:

@dataclassclass A:    value: int    @singledispatchmethod    def dispatch(self, x):        return id(self)t1 = A(1)t1.dispatch(10)

Right after running this code the cache (e.g._method_cache) will contain a single key-value pair: the key isid(t1) and the value is the_method object created in the dispatcher. The_method has a positive reference count (since_method.__self_reference holds a reference to_method).
When the garbage collector runs (seePython internal documentation on GC for details) the_method is part of a cycle of objects (e.g._method ->_method.__self_reference ->_method) and is removed. Since there the_method object is no longer alive, theWeakValueDictionary cache_method_cache will remove the key-value pair.

We can address the issue by creating a reference fromobj to the entry in the cache. Something like:

        try:            setattr(obj, f'_singledispatchmethod_cache_{id(self)}', _method)  # create strong reference to _method which is not an isolated cycle. see gh-127751        except Exception as ex:            # frozen object, disable the cache            self._method_cache = None

This works, but has the disadvantage that we are adding some (private) attributes in the object instance.

Note: the documentationhttps://docs.python.org/3/library/weakref.html#weakref.WeakValueDictionary states:

Mapping class that references values weakly. Entries in the dictionary will be discarded when no strong reference to the value exists any more.

That text is a bit misleading: there is a reference to_method, but still it is removed. I will make a separate PR to address this.

@dg-pb
Copy link
Contributor

dg-pb commentedJan 8, 2025
edited
Loading

I see now.
I think simplifying this might be worthwhile.

Also, I would like the method to be cached for the lifetime of my instance, doesn't it currently garbage collect perfectly good cached methods?

This works, but has the disadvantage that we are adding some (private) attributes in the object instance.

What about the same way ascached_property works?

classsingledispatchmethod:def__set_name__(self,owner,name):self.attrname=namedef__get__(self,instance,owner=None):try:cache=instance.__dict__exceptAttributeError:# disable caching going forwardelse:method=cache.get(self.attrname)ifmethodisnotNone:returnmethodelse:                ...

return_method

dispatch=self.dispatcher.dispatch
funcname=getattr(self.func,'__name__','singledispatchmethod method')
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe moving this line just above theTypeError would be a bit more readable?
The performance benefit this offers would only matter if there was a code which actively depends on handling this exception.

@eendebakpt
Copy link
ContributorAuthor

eendebakpt commentedJan 8, 2025
edited
Loading

I see now. I think simplifying this might be worthwhile.

Also, I would like the method to be cached for the lifetime of my instance, doesn't it currently garbage collect perfectly good cached methods?

Yes, that is why I did not like the approach in this PR.

What about the same way ascached_property works?

Nice idea. I created an alternative PR for this variation.

@eendebakpt
Copy link
ContributorAuthor

Issue resolved in other PRs, closing

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

Reviewers

@AlexWaygoodAlexWaygoodAwaiting requested review from AlexWaygood

2 more reviewers

@dg-pbdg-pbdg-pb left review comments

@vodikvodikvodik approved these changes

Reviewers whose approvals may not affect merge requirements

Assignees

No one assigned

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

3 participants

@eendebakpt@vodik@dg-pb

[8]ページ先頭

©2009-2025 Movatter.jp