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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inheriting from ABCs makes classes slower. #46102
Comments
Adding numbers.Real to Decimal's base classes almost doubles the time |
Is this for 2.6 or 3.0? |
I've only verified the behavior on 2.6, but I suspect it's true for both. |
__instancecheck__ contains unnecessary code. If we restrict ABCs to new Old: def __instancecheck__(cls, instance):
"""Override for isinstance(instance, cls)."""
return any(cls.__subclasscheck__(c)
for c in {instance.__class__, type(instance)})
New:
def __instancecheck__(cls, instance):
"""Override for isinstance(instance, cls)."""
# safe a function call for common case
if instance.__class__ in cls._abc_cache:
return True
return cls.__subclasscheck__(instance.__class__) |
What change is responsible for the speedup? The cache check or removing It's also important to find out where __instancecheck__ is called; I |
without abc: with numbers.Real subclass:
$ time ./python Lib/test/regrtest.py test_decimal
real 0m16.232s
user 0m15.241s
sys 0m0.384s Proposed patch: Only with if instance.__class__ in cls._abc_cache: return True Wow, __instancecheck__ must be called a *lot* of times. |
The patch implements the rich cmp slots for <, <=, > and >= required for |
I measured various implementations of instancecheck using # Current code
return any(cls.__subclasscheck__(c)
for c in set([instance.__class__, type(instance)]))
isinstance(3, Rational): 4.65 usec
isinstance(r, Rational): 7.47 usec
# The best we can do simply in Python
return cls.__subclasscheck__(instance.__class__)
isinstance(3, Rational): 2.08 usec
isinstance(r, Rational): 1.72 usec
# Preserve behavior, simply
return (cls.__subclasscheck__(instance.__class__) or
cls.__subclasscheck__(type(instance)))
isinstance(3, Rational): 3.03 usec
isinstance(r, Rational): 1.8 usec
# Preserve behavior, complexly
ic = instance.__class__
if cls.__subclasscheck__(ic):
return True
t = type(instance)
return t is not ic and cls.__subclasscheck__(t)
isinstance(3, Rational): 2.38 usec
isinstance(r, Rational): 1.86 usec
# Inlined for new-style classes
subclass = instance.__class__
if subclass in cls._abc_cache:
return True
type_ = type(instance)
if type_ is subclass:
if (cls._abc_negative_cache_version ==
ABCMeta._abc_invalidation_counter and
subclass in cls._abc_negative_cache):
return False
return cls.__subclasscheck__(subclass)
return (cls.__subclasscheck__(subclass) or
cls.__subclasscheck__(type_))
isinstance(3, Rational): 2.26 usec
isinstance(r, Rational): 1.49 usec |
Whoa! I thought we had arrived at a decision to leave decimal alone. |
Right. Decimal was just the place I noticed the problem first. Now it |
I've committed the inlined option as r60762. |
Guido and I discussed this, and the next step seems to be to rewrite |
I'd like a second opinion about whether it's a good idea to commit the ./python.exe -m timeit -s 'import abc' -s 'class Foo(object): up from 2.5us to 0.201us. For comparison: $ ./python.exe -m timeit -s 'import abc' -s 'class Foo(object): pass'
'Foo()'
10000000 loops, best of 3: 0.203 usec per loop
$ ./python.exe -m timeit -s 'import abc' -s 'class Foo(object):' -s '
def __new__(cls): return super(Foo, cls).__new__(cls)' 'Foo()'
1000000 loops, best of 3: 1.18 usec per loop
$ ./python.exe -m timeit -s 'import abc' -s 'from decimal import
Decimal' 'Decimal()'
100000 loops, best of 3: 9.51 usec per loop After this patch, the only slowdown I can find is an extra .5us in |
Since there were no comments, I submitted the patch as r61098. I think |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: