New submission from Federico Caselli <cfederic...@gmail.com>:

The implementation of singledispatchmethod is significantly slower (~4x) than 
the normal singledispatch version

Using timeit to test this example case:

    from functools import singledispatch, singledispatchmethod
    import timeit

    class Test:
        @singledispatchmethod
        def go(self, item, arg):
            print('general')
        
        @go.register
        def _(self, item:int, arg):
            return item + arg

    @singledispatch
    def go(item, arg):
        print('general')

    @go.register
    def _(item:int, arg):
        return item + arg

    print(timeit.timeit('t.go(1, 1)', globals={'t': Test()}))
    print(timeit.timeit('go(1, 1)', globals={'go': go}))


Prints on my system.

    3.118346
    0.713173

Looking at the singledispatchmethod implementation I believe that most of the 
difference is because a new function is generated every time the method is 
called.

Maybe an implementation similar to cached_property could be used if the class 
has __dict__ attribute?
Trying this simple patch

    diff --git a/Lib/functools.py b/Lib/functools.py
    index 5cab497..e42f485 100644
    --- a/Lib/functools.py
    +++ b/Lib/functools.py
    @@ -900,6 +900,7 @@ class singledispatchmethod:

            self.dispatcher = singledispatch(func)
            self.func = func
    +        self.attrname = None

        def register(self, cls, method=None):
            """generic_method.register(cls, func) -> func
    @@ -908,6 +909,10 @@ class singledispatchmethod:
            """
            return self.dispatcher.register(cls, func=method)

    +    def __set_name__(self, owner, name):
    +        if self.attrname is None:
    +            self.attrname = name
    +
        def __get__(self, obj, cls=None):
            def _method(*args, **kwargs):
                method = self.dispatcher.dispatch(args[0].__class__)
    @@ -916,6 +921,7 @@ class singledispatchmethod:
            _method.__isabstractmethod__ = self.__isabstractmethod__
            _method.register = self.register
            update_wrapper(_method, self.func)
    +        obj.__dict__[self.attrname] = _method
            return _method

        @property

improves the performance noticeably

    0.9720976
    0.7269078

----------
components: Library (Lib)
messages: 371594
nosy: CaselIT
priority: normal
severity: normal
status: open
title: singledispatchmethod significantly slower than singledispatch
type: performance
versions: Python 3.8

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue40988>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to