[sqlalchemy] Re: SA 0.3.x: performance issues = proposal

2006-12-08 Thread Sébastien LELONG
As attachement, here's the patch (against rev 2132). It's local to 
sqlalchemy/orm/, so:

cd /sqlalchemy/orm
patch -p0  attribute_cache.patch


About the patch itself:
 1. the cache should be a WeakKeyDictionary
OK, done. Performances are still OK, differences between built-in dict are 
negligible.

 2. the cache needs to be cleared for a particular cache (class ?)
If I understand right, while calling register_attribute(), the cache will be 
cleared for the given class, if this one has been cached. Is there anywhere 
else the cache needs to cleared ?

 3. the cache should *probably* be at the module level, and not within
 the AttributeManager itself; SA uses only a single AttributeManager per
 application so it probably doesnt matter
I let the cache within the AttributeManager... Things can be moved at the 
module level, but if it's not necessary right now, it might not be a good 
idea to do it right now (when it will be necessary, the design may have been 
changed and modifications not valid anymore).

 4. the raise should use an exception class of some kind
I've made it raise a TypeError... Yes, string based exceptions are very a bad 
thing !


Attributes are cached while using the managed_attributes() *and* 
noninherited_managed_attributes() funcs. Those are very similar. I hesitated 
refactor them, but centralize cache management is probably a good thing. This 
is the purpose of the second patch, which include this refactoring. As 
expected, performances decrease. Here's the traditionnal benchmark results:

SA 0.3.1, rev 2127:
total time 4.29376721382

real0m5.420s
user0m4.088s
sys 0m0.108s

SA with attr. cache, no refactoring (~2X faster, back closed to 0.2.8 
speed):
total time 2.34819602966

real0m3.013s
user0m2.344s
sys 0m0.088s

SA with attr. cache, with refactoring (30% slower than without 
refactoring)
total time 3.05679416656

real0m3.747s
user0m2.988s
sys 0m0.068s


It's up to you choosing the patch ! IMO, I'm *not* in favor to use refactoring 
in this case :). 

Finally, I've put a clear_attribute_cache func which, ... clear the attribute 
cache. While client code may not have to worry about caching, it may need to 
clear it...


 thanks much, this is the kind of user involvement i like.
Well, you're very welcome ! Glad to help !


Cheers,

Seb
-- 
Sébastien LELONG
sebastien.lelong[at]sirloon.net


P.S:

 im beginning
 to suspect that yield introduces overhead vs. a straight list ?
 (python interpreter storing stack frames ?  dunno).
dunnotoo :)


--~--~-~--~~~---~--~~
 You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---
Index: attributes.py
===
--- attributes.py	(révision 2132)
+++ attributes.py	(copie de travail)
@@ -609,8 +609,18 @@
 
 class AttributeManager(object):
 allows the instrumentation of object attributes.  AttributeManager is stateless, but can be
-overridden by subclasses to redefine some of its factory operations.
+overridden by subclasses to redefine some of its factory operations. Also be aware AttributeManager
+will cache attributes for a given class, allowing not to determine those for each objects (used
+in managed_attributes() and noninherited_managed_attributes()). This cache is cleared for a given class
+while calling register_attribute(), and can be cleared using clear_attribute_cache()
 
+def __init__(self):
+# will cache attributes, indexed by class objects
+self._attribute_cache = weakref.WeakKeyDictionary()
+
+def clear_attribute_cache(self):
+self._attribute_cache.clear()
+
 def rollback(self, *obj):
 retrieves the committed history for each object in the given list, and rolls back the attributes
 each instance to their original value.
@@ -639,21 +649,31 @@
 
 def managed_attributes(self, class_):
 returns an iterator of all InstrumentedAttribute objects associated with the given class.
+if self._attribute_cache.has_key(class_):
+return self._attribute_cache[class_]
+
+self._attribute_cache[class_] = []   
 if not isinstance(class_, type):
-raise repr(class_) +  is not a type
+raise TypeError(repr(class_) +  is not a type)
 for key in dir(class_):
 value = getattr(class_, key, None)
 if 

[sqlalchemy] Re: SA 0.3.x: performance issues = proposal

2006-12-06 Thread Michael Bayer

i think that is a great idea !

can you make a patch for me ?

here are some things i think it should have:

1. the cache should be a WeakKeyDictionary, if possible, so that an
application which creates classes on the fly doesnt run out of memory.
this adds a micro-amount of overhead vs. a dictionary but i think its
negligible (compared to all the isintance calls and yields we are
losing)
2. the cache needs to be cleared for a particular cache whenever the
attribute manager is accessed to add or modify properties on the class
(i.e. the register_attribute method)
3. the cache should *probably* be at the module level, and not within
the AttributeManager itself; SA uses only a single AttributeManager per
application so it probably doesnt matter, but a class itself is going
to have the same properties regardless of which AttributeManager
accesses it (and also resets the cache)
4. the raise should use an exception class of some kind (TypeError?
exceptions.ArgumentError?)

as far as the yield, ive no problem getting rid of that.  im beginning
to suspect that yield introduces overhead vs. a straight list ?
(python interpreter storing stack frames ?  dunno).

thanks much, this is the kind of user involvement i like.


--~--~-~--~~~---~--~~
 You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---