please try out this patch:

http://www.sqlalchemy.org/trac/attachment/ticket/2778/2778.patch


which refactors this particular system to not require the production of a new 
object per instance, which is the slowest part of this, and also inlines the 
work of assembling the callable.  This should give you 50% or more method call 
improvement.  if this is enough, this might be OK for 0.8.





On Jul 11, 2013, at 1:40 PM, Michael Bayer <mike...@zzzcomputing.com> wrote:

> well what kind of data are we talking about?  defer()'s use case was for 
> binary large objects and such, fields that are many K/Megs in size.  if 
> you're deferring a bunch of ints, then yes it's not optimized very well for 
> that.
> 
> Half of the overhead could be easily fixed here, creating those 
> LoadDeferredColumns objects could be offloaded to a later point.    The other 
> half, setting up that callable, I'd have to spend some time reviewing the use 
> cases here.  The difference between an attribute that is "deferred" vs. one 
> that is "expired" is that if you access some other expired attribute, the 
> "deferred" attribute will still not load - because the use case is, you 
> really don't want this BLOB column to load unless you touch it specifically.  
>  So to get that instruction into the state, "don't load these keys even on an 
> unexpire", uses some kind of method call on every state.    
> InstanceState._set_callable could be inlined more here to do less work, 
> instructions up to the loader process just to populate a key in a dictionary 
> maybe, though these reorganizations can destabilize the code.   it's not 
> something I'd be comfortable doing in 0.8, the ticket I created 
> (http://www.sqlalchemy.org/trac/ticket/2778) has any potential work here for 
> 0.9.
> 
> The other way to go here is to provide a query option that explicitly 
> delivers the attribute as "expired" as opposed to "deferred", looking at how 
> that works right now I can give you the recipe below, but it still involves a 
> function call per column so that the InstanceState knows the attribute is 
> "expired".
> 
> 
> from sqlalchemy.orm.strategies import DeferredOption, DeferredColumnLoader
> 
> 
> class DontLoadColumnOption(DeferredOption):
>     def get_strategy_class(self):
>         return NoColumnLoader
> 
> 
> class NoColumnLoader(DeferredColumnLoader):
>     def create_row_processor(self, context, path, mapper, row, adapter):
>         if not self.is_class_level:
>             def set_deferred_for_local_state(state, dict_, row):
>                 state.callables[self.key] = state
>             return set_deferred_for_local_state, None, None
>         else:
>             return super(NoColumnLoader, self).create_row_processor(
>                                     context, path, mapper, row, adapter)
> 
> if __name__ == '__main__':
>     from sqlalchemy import *
>     from sqlalchemy.orm import *
>     from sqlalchemy.ext.declarative import declarative_base
> 
>     Base = declarative_base()
> 
>     class A(Base):
>         __tablename__ = 'a'
> 
>         id = Column(Integer, primary_key=True)
> 
>         x = Column(Integer)
>         y = Column(Integer)
>         z = Column(Integer)
>         q = Column(Integer)
> 
>     e = create_engine("sqlite://", echo=True)
>     Base.metadata.create_all(e)
>     s = Session(e)
>     s.add_all([
>         A(x="x%d" % i, y="y%d" % i, z="z%d" % i, q="q%d" % i)
>         for i in xrange(1000)
>     ])
>     s.commit()
>     s.close()
> 
>     loaded = s.query(A).options(DontLoadColumnOption("y"),
>                     DontLoadColumnOption("z")).order_by(A.id).all()
> 
>     for a in loaded:
>         assert 'y' not in a.__dict__
>         assert 'z' not in a.__dict__
>         assert 'x' in a.__dict__
>         assert 'q' in a.__dict__
> 
>         assert a.z == "z%d" % (a.id - 1), a.z
> 
> 
> 
> 
> 
> 
> 
> 
> 
> 
> 
> 
> 
> On Jul 11, 2013, at 10:23 AM, "Gombas, Gabor" 
> <gabor.gom...@morganstanley.com> wrote:
> 
>> 
>> I did need the objects, not just the raw data, otherwise I’d had to 
>> duplicate a bunch of existing code which expected full-blown objects to 
>> operate on. Modifying the mapper is not really an option unless the majority 
>> of the users have the same requirements, otherwise I end up having to add a 
>> huge amount of undefer() calls everywhere else (and the query storm caused 
>> by missing an undefer() would be much more painful). Maybe the documentation 
>> of defer() could mention that the use of the option can in fact reduce 
>> performance, because it’s not intuitive that loading unneeded data is 
>> cheaper than not loading it.
>>  
>> From: sqlalchemy@googlegroups.com [mailto:sqlalchemy@googlegroups.com] On 
>> Behalf Of Michael Bayer
>> Sent: 11 July 2013 15:34
>> To: sqlalchemy@googlegroups.com
>> Subject: Re: [sqlalchemy] The cost of defer()
>>  
>> the path would be to figure out if the logic of a per-query defer option can 
>> somehow be linked to the attribute when it hits its normal refresh logic - 
>> if those attribute were set up as "deferred" at the mapper config level 
>> (where deferred is usually used), you wouldn't see this overhead since the 
>> deferred loader would be the default callable.    But if you only need a few 
>> attributes why not just go the other way and query for those attributes 
>> directly?   that would save you way more overhead than even if we removed 
>> all overhead from defer(), since the most expensive thing is all the mapper 
>> identity map logic that takes place when full entities are loaded.
>>  
>>  
>> On Jul 11, 2013, at 4:02 AM, "Gombas, Gabor" 
>> <gabor.gom...@morganstanley.com> wrote:
>> 
>> 
>>  
>> Hi,
>>  
>> I wrote a query joining a couple of tables, returning over a hundred 
>> thousand rows. Since I only needed to access a couple of the attributes of 
>> the returned objects for this specific use case, I thought to use a dozen or 
>> so Query.options(defer(…)) calls to avoid loading the unneeded columns. But 
>> to my surprise, the query became much slower. Profiling attributed almost 
>> all the extra time to set_deferred_for_local_state() and 
>> LoadDeferredColumns.__init__():
>>  
>>    827855   15.668    0.000   27.068    0.000 
>> …/SQLAlchemy-0.8.1-py2.6-linux-x86_64.egg/sqlalchemy/orm/strategies.py:184(set_deferred_for_local_state)
>>    827855   10.524    0.000   10.524    0.000 
>> …/SQLAlchemy-0.8.1-py2.6-linux-x86_64.egg/sqlalchemy/orm/strategies.py:259(__init__)
>>  
>> If defer() is so expensive, then it is not very useful. Would it be possible 
>> to make it cheaper?
>>  
>> Gabor
>>  
>>  
>> 
>> NOTICE: Morgan Stanley is not acting as a municipal advisor and the opinions 
>> or views contained herein are not intended to be, and do not constitute, 
>> advice within the meaning of Section 975 of the Dodd-Frank Wall Street 
>> Reform and Consumer Protection Act. If you have received this communication 
>> in error, please destroy all electronic and paper copies and notify the 
>> sender immediately. Mistransmission is not intended to waive confidentiality 
>> or privilege. Morgan Stanley reserves the right, to the extent permitted 
>> under applicable law, to monitor electronic communications. This message is 
>> subject to terms available at the following link: 
>> http://www.morganstanley.com/disclaimers If you cannot access these links, 
>> please notify us by reply message and we will send the contents to you. By 
>> messaging with Morgan Stanley you consent to the foregoing.
>>  
>>  
>>  
>>  
>> -- 
>> You received this message because you are subscribed to the Google Groups 
>> "sqlalchemy" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to sqlalchemy+unsubscr...@googlegroups.com.
>> To post to this group, send email to sqlalchemy@googlegroups.com.
>> Visit this group at http://groups.google.com/group/sqlalchemy.
>> For more options, visit https://groups.google.com/groups/opt_out.
>>  
>>  
>>  
>> -- 
>> You received this message because you are subscribed to the Google Groups 
>> "sqlalchemy" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email tosqlalchemy+unsubscr...@googlegroups.com.
>> To post to this group, send email to sqlalchemy@googlegroups.com.
>> Visit this group at http://groups.google.com/group/sqlalchemy.
>> For more options, visit https://groups.google.com/groups/opt_out.
>>  
>>  
>> 
>> 
>> 
>> NOTICE: Morgan Stanley is not acting as a municipal advisor and the opinions 
>> or views contained herein are not intended to be, and do not constitute, 
>> advice within the meaning of Section 975 of the Dodd-Frank Wall Street 
>> Reform and Consumer Protection Act. If you have received this communication 
>> in error, please destroy all electronic and paper copies and notify the 
>> sender immediately. Mistransmission is not intended to waive confidentiality 
>> or privilege. Morgan Stanley reserves the right, to the extent permitted 
>> under applicable law, to monitor electronic communications. This message is 
>> subject to terms available at the following 
>> link:http://www.morganstanley.com/disclaimers If you cannot access these 
>> links, please notify us by reply message and we will send the contents to 
>> you. By messaging with Morgan Stanley you consent to the foregoing.
>> 
>> 
>> 
>> 
>> -- 
>> You received this message because you are subscribed to the Google Groups 
>> "sqlalchemy" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email tosqlalchemy+unsubscr...@googlegroups.com.
>> To post to this group, send email to sqlalchemy@googlegroups.com.
>> Visit this group at http://groups.google.com/group/sqlalchemy.
>> For more options, visit https://groups.google.com/groups/opt_out.    
> 
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to sqlalchemy+unsubscr...@googlegroups.com.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> Visit this group at http://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/groups/opt_out.
>  
>  

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to