Стоимость перезаписи __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 ответа

Решение

Не полный ответ, но некоторые предложения:

  1. Можете ли вы исключить сравнение значений? Конечно, это изменение функции вашей реализации. Но издержки во время выполнения будут еще хуже, если в атрибутах будут храниться более сложные объекты, чем целые числа.

  2. Каждый вызов метода через self необходимо пройти полную проверку порядка разрешения методов. Я не знаю, может ли Python сам выполнять кеширование MRO. Вероятно, не из-за принципа динамического типа. Таким образом, вы должны быть в состоянии уменьшить некоторые накладные расходы, изменив любой self.method(args) в classname.method(self, args), Это устраняет накладные расходы MRO из вызовов. Это относится к self.setModified() в вашем settattr() реализация. В большинстве мест вы уже сделали это со ссылками на object,

  3. Каждый вызов функции занимает время. Вы можете устранить их и, например, двигаться 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
Другие вопросы по тегам