On Fri, Apr 26, 2013 at 2:04 PM, Michael Bayer <mike...@zzzcomputing.com> wrote:
>>> are you referring to sqlalchemy/orm/loading.py ?   I'd be pretty impressed 
>>> if you can find significant optimizations there which don't break usage 
>>> contracts.    I've spent years poring over profiles and squeezing every 
>>> function call possible out of that system, sometimes producing entirely new 
>>> approaches that I just had to throw out since they didn't work.   It has 
>>> been rewritten many times.   Some background on the approach is at 
>>> http://www.aosabook.org/en/sqlalchemy.html, "20.7. Query and Loading 
>>> Behavior".
>>
>>
>> I know... I'm talking micro-optimization. Pre-binding globals in tight
>> loops, for instance, like:
>>
>> def filter_fn(x, tuple=tuple, zip=zip):
>>    return tuple(...)
>>
>> This is of course only worth it for really really hot loops. That's
>> why I'm profiling. Maybe it's been done already for all the hottest
>> loops.
>>
>> Then there's the possibility to replace some list comprehensions with
>> itertools, which besides not building a temp list, would also run
>> entirely in C. This also only makes a difference only on very tight,
>> builtin-laden loops.
>>
>> I have an app here that really stresses that part of the ORM, so I can
>> profile rather easily. In previous profiles, I remember seeing
>> Query.instances near the top, and all the optimizations I mentioned
>> above could be applied there, if they make any difference I'll tell.
>
> the real bottleneck in loading is the loading.instances() function.  I have 
> tried for years to reduce overhead in it.  Writing it in C would be best, but 
> then again Pypy aims to solve the problem of FN overhead, pre-binding, and 
> such.   I don't want to work against Pypy too much.

That makes the proposition tricky. I don't know PyPy's performance
characteristics that well. I assume pre-binding wouldn't hurt PyPy
much, since loop traces would be nearly the same, but I've never
tested.

Pre-binding in filter_fn improves its runtime ten-fold. Actually,
pre-binding and replacing tuple(genexpr) by tuple([compexpr]), since
genexprs are rather slow compared to list compehensions. The
improvement accounts for 1% of my test's runtime, so if it hurts PyPy,
it might not be so great an optimization (if it doesn't, though, it's
a very cheap one, and it could be applicable in other places). This
particular one helps in the case of query(Column, Column, Column),
which I use a lot.

Note, however, that my test is 40% waiting on the DB, so CPU usage
impact would be proportionally bigger, especially with parallel
workers (I'm using just one thread when profiling though).

Doing those small optimizations to WeakIdentityMap (another one whose
methods are called an obscenely large amount of times), I get about
10% speedup on those. I imagine that could count in some situations.

Ultimately, though, it's InstrumentedAttribute.__get__ the one sucking
up 30% of alchemy-bound CPU time. I guess there's little that can be
done, since it's necessary to track state changes. But there's a neat
property of descriptors, where if they don't implement __get__, then
they don't take precedence over the instance's dict.

This is very interesting, and handy, since when instance_dict is
attrgetter('__dict__'), then, for regular ColumnPropertys, instead of
using InstrumentedAttribute, I can replace that with an
"InstrumentedWriteAttribute" that has no get. This means, all of a
sudden, no overhead for simple attribute access.

I've tested it and it mostly works. There's the "instance_dict is
attrgetter('__dict__')" thing hanging over my head, and the more
serious issue of lazy attributes being mostly broken, but it's an
interesting POC IMHO.

Anyway, with that (fragile) change, I get a speedup of 10% overall
runtime, and about 50% alchemy-specific runtime. Considering I knew
about attribute access' slowness and avoided it in my test, that has
to account for something worth looking into? (before optimizing for
attribute access slowness, the test was about 3 times slower IIRC -
*times* - and it does a hefty amount of regex processing beyond
handling attributes)

-- 
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?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to