Стоимость перезаписи __setattr__ () была слишком высокой
Я хочу сэкономить время и пометить объект как измененный, поэтому я написал класс и переопределил его __setattr__
функция.
import time
class CacheObject(object):
__slots__ = ('modified', 'lastAccess')
def __init__(self):
object.__setattr__(self,'modified',False)
object.__setattr__(self,'lastAccess',time.time())
def setModified(self):
object.__setattr__(self,'modified',True)
object.__setattr__(self,'lastAccess',time.time())
def resetTime(self):
object.__setattr__(self,'lastAccess',time.time())
def __setattr__(self,name,value):
if (not hasattr(self,name)) or object.__getattribute__(self,name)!=value:
object.__setattr__(self,name,value)
self.setModified()
class example(CacheObject):
__slots__ = ('abc',)
def __init__(self,i):
self.abc = i
super(example,self).__init__()
t = time.time()
f = example(0)
for i in range(100000):
f.abc = i
print(time.time()-t)
Я измерил время процесса, и это заняло 2 секунды. Когда я закомментировал переопределенную функцию, время обработки составило 0,1 секунды, я знаю, что переопределенная функция будет медленнее, но почти в 20 раз разрыв слишком велик. Я думаю, что я должен сделать что-то не так.
принять предложение от cfi
1. оцените условие if
def __setattr__(self,name,value):
# if (not hasattr(self,name)) or object.__getattribute__(self,name)!=value:
object.__setattr__(self,name,value)
self.setModified()
время выполнения до 1,9, немного улучшенное, но пометка измененного объекта, если он не изменен, будет стоить дороже в другом процессе, поэтому не вариант.
2. измените self.func на classname.func(self)
def __setattr__(self,name,value):
if (not hasattr(self,name)) or object.__getattribute__(self,name)!=value:
object.__setattr__(self,name,value)
CacheObject.setModified(self)
время работы 2.0 . так что ничего не изменилось
3) извлечь заданную модифицированную функцию
def __setattr__(self,name,value):
if (not hasattr(self,name)) or object.__getattribute__(self,name)!=value:
object.__setattr__(self,name,value)
object.__setattr__(self,'modified',True)
object.__setattr__(self,'lastAccess',time.time())
время работы до 1,2!! Это здорово, это экономит почти 50% времени, хотя стоимость все еще высока.
3 ответа
Не полный ответ, но некоторые предложения:
Можете ли вы исключить сравнение значений? Конечно, это изменение функции вашей реализации. Но издержки во время выполнения будут еще хуже, если в атрибутах будут храниться более сложные объекты, чем целые числа.
Каждый вызов метода через
self
необходимо пройти полную проверку порядка разрешения методов. Я не знаю, может ли Python сам выполнять кеширование MRO. Вероятно, не из-за принципа динамического типа. Таким образом, вы должны быть в состоянии уменьшить некоторые накладные расходы, изменив любойself.method(args)
вclassname.method(self, args)
, Это устраняет накладные расходы MRO из вызовов. Это относится кself.setModified()
в вашемsettattr()
реализация. В большинстве мест вы уже сделали это со ссылками наobject
,Каждый вызов функции занимает время. Вы можете устранить их и, например, двигаться
setModified
функциональность в__setattr__
сам.
Дайте нам знать, как меняется время для каждого из них. Я бы разделил эксперимент.
Редактировать: Спасибо за временные числа.
Накладные расходы могут показаться радикальными (кажется, что в 10 раз). Однако поместите это в общую среду выполнения. Другими словами: сколько времени вы потратите на настройку отслеживаемых атрибутов и сколько времени потратите в другом месте?
В однопоточном приложении закон Амдала - простое правило, чтобы оправдать ожидания. Иллюстрация: если 1/3 времени тратится на установку атрибутов, а 2/3 - на другие. Тогда замедление настройки атрибута в 10 раз только замедлит 30%. Чем меньше процент времени, проведенного с атрибутами, тем меньше нам нужно заботиться. Но это может не помочь вам, если ваш процент высок...
Старый вопрос, но стоит обновить.
Я столкнулся с той же проблемой с pydantic, используя python 3.6.
object.__setattr__(self, name, value)
просто медленнее, чем обычная установка атрибута в классе. Нет очевидного способа обойти это.
Если производительность важна, единственная возможность - продолжать звонить object.__setattr__(self, name, value)
до абсолютного минимума в классах, где вам нужно переопределить _setattr_
,
Переопределение __setattr__
здесь, кажется, не имеет никакой функции. У вас есть только два атрибута, измененный и lastAccess. Это означает, что это единственные атрибуты, которые вы можете установить, так почему бы вам переопределить __setattr__
? Просто установите атрибуты напрямую.
Если вы хотите, чтобы что-то происходило при установке атрибута, сделайте его свойством с помощью установщика и получателя. Это проще и намного менее волшебно.
class CacheObject(object):
__slots__ = ('modified', 'lastAccess')
def __init__(self):
self.modified = False
self.lastAccess = time.time()
def setModified(self):
self.modified = True
self.lastAccess = time.time()
def resetTime(self):
self.lastAccess = time.time()
class example(CacheObject):
__slots__ = ('_abc',)
def __init__(self,i):
self._abc = i
super(example,self).__init__()
@property
def abc(self):
self.resetTime()
return self._abc
@abc.setter
def abc(self, value):
self.setModified()
self._abc = value