2012-11-02 3 views
2

Je veux enregistrer l'heure et marquer l'objet comme modifié, j'ai donc écrit une classe et remplacer sa fonction __setattr__.le coût de réécriture __setattr__() était trop élevé

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) 

J'ai mesuré le temps de traitement et cela a pris 2 secondes. Quand j'ai commenté sur la fonction surchargée, le temps de traitement était de 0,1 seconde, je sais que la fonction surchargée serait plus lente mais presque 20 fois l'écart est trop important. Je pense que je dois me tromper.

prendre la suggestion de tpi

1.elimate la condition if

def __setattr__(self,name,value): 
#  if (not hasattr(self,name)) or object.__getattribute__(self,name)!=value: 
      object.__setattr__(self,name,value) 
      self.setModified() 

le temps de fonctionnement à 1,9, un peu améliorer, mais marquer l'objet modifié si cela ne change coûterait plus cher dans d'autres processus, donc pas une option.

2.CHANGER self.func à classname.func (auto)

def __setattr__(self,name,value): 
    if (not hasattr(self,name)) or object.__getattribute__(self,name)!=value: 
     object.__setattr__(self,name,value) 
     CacheObject.setModified(self) 

de temps d'exécution est de 2,0 .donc rien n'a vraiment changé

3) Extrait fonction setmodified

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()) 

fonctionnement Le temps passé à 1,2 !! C'est génial, cela permet d'économiser près de 50% du temps, même si le coût reste élevé.

+0

Merci pour les commentaires de vos numéros! Nous pourrions argumenter qu'il y a deux appels de fonction impliqués, et puisque la suppression d'un a réduit l'overhead de 50%, vous pourriez être coincé avec le surcoût restant. Mais peut-être que quelqu'un d'autre a plus d'idées. – cfi

Répondre

1

Pas une réponse complète, mais quelques suggestions:

  1. Pouvez-vous éliminer la comparaison de la valeur? Bien sûr, c'est un changement de fonctionnalité de votre implémentation. Mais la surcharge de l'exécution sera encore pire si des objets plus complexes que des entiers sont stockés dans des attributs.

  2. Chaque appel à une méthode via self doit passer par une vérification complète de l'ordre de résolution de la méthode. Je ne sais pas si Python pourrait faire une mise en cache MRO elle-même. Probablement pas à cause du principe des types-étant-dynamiques. Ainsi, vous devriez être en mesure de réduire certains frais généraux en changeant self.method(args)classname.method(self, args). Cela supprime le surdébit MRO des appels. Ceci s'applique à self.setModified() dans votre implémentation settattr(). Dans la plupart des endroits, vous avez déjà fait cela avec des références à object.

  3. Chaque appel de fonction prend du temps. Vous pourriez les éliminer et, par exemple, déplace la fonctionnalité setModified en __setattr__ lui-même.

Faites-nous savoir comment le minutage change pour chacun d'entre eux. Je diviserais l'expérience.

Édition: Merci pour les numéros de temporisation.

La surcharge peut sembler drastique (encore un facteur de 10 semble). Cependant mettre cela en perspective de l'exécution globale. En d'autres termes: Dans quelle mesure le temps d'exécution global sera-t-il consacré à définir ces attributs suivis et combien de temps est-il dépensé ailleurs?

Dans une application à un seul fil Amdahl's Law is a simple rule pour définir les attentes directement.Une illustration: Si 1/3 du temps est passé à définir des attributs, et 2/3 à faire autre chose. Le ralentissement de 10x du paramètre d'attribut ne fera que ralentir les 30%. Plus le pourcentage de temps passé avec les attributs est faible, moins nous devons en prendre soin. Mais cela peut ne pas vous aider du tout si votre pourcentage est élevé ...

+0

c'est la meilleure amélioration que je pourrais obtenir maintenant, je suppose, je passerai à l'étape suivante, merci. – Max

0

La surcharge __setattr__ semble ne pas avoir de fonction. Vous avez seulement deux attributs, modifié et lastAccess. Cela signifie que ce sont les seuls attributs que vous pouvez définir, alors pourquoi substituer __setattr__? Définissez simplement les attributs directement.

Si vous souhaitez que quelque chose se produise lors de la définition d'un attribut, définissez-le comme propriété avec un accesseur et un accesseur. C'est plus facile et beaucoup moins magique.

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 
+0

Le CacheObject est une super classe, sa sous-classe a différentes variables à stocker, elles ont toutes le même mécanisme. – Max

+0

@Max: Alors vous cachez la magie qui se produit à partir des sous-classes. Je ferais à la place de chaque attribut une propriété, et simplement définir l'accès/modifier dans ces propriétés. C'est une duplication, mais une duplication non magique. Ça ne va pas te mordre dans le pied. –

+0

voulez-vous dire utiliser @property et setter? J'ai essayé, le temps est à 4.9s – Max

0

Ancienne question mais vaut la peine d'être mise à jour.

J'ai rencontré le même problème avec pydantic en utilisant python 3.6.

object.__setattr__(self, name, value) est simplement plus lent que de définir un attribut sur une classe normalement. Aucun moyen apparent de contourner cela.

Si les performances sont importantes, la seule option consiste à limiter les appels à object.__setattr__(self, name, value) dans les classes où vous devez remplacer _setattr_.

Questions connexes