Magic Optimisation
Bengt Richter
bokr at oz.net
Mon Sep 5 21:38:44 EDT 2005
On Mon, 05 Sep 2005 21:39:31 GMT, bokr at oz.net (Bengt Richter) wrote:
>On 5 Sep 2005 07:27:41 -0700, "Paul McGuire" <ptmcg at austin.rr.com> wrote:
>
>>I still think there are savings to be had by looping inside the
>>try-except block, which avoids many setup/teardown exception handling
>>steps. This is not so pretty in another way (repeated while on
>>check()), but I would be interested in your timings w.r.t. your current
>>code.
>>
>> def loop(self):
>> self_pool_popleft = self.pool.popleft
>> self_pool_append = self.pool.append
>> self_call_exit_funcs = self.call_exit_funcs
>> check = self.pool.__len__
>> while check() > 0:
>> try:
>> while check() > 0:
>> task = self_pool_popleft()
>> task.next()
>> self_pool_append(task)
>> except StopIteration:
>> self_call_exit_funcs(task)
>>
>
>Why not let popleft trigger an exception out of while True instead,
>and prevent tasks from raising StopIteration, and let them yield a None
>to indicate keep scheduling with no special action, and something else
>for optional differentiation of various exit options, e.g., zero for die,
>and nonzero for suspension waiting for event(s) E.g., a returned integer
>could be an event mask or single index (+ vs -) for thing(s) to wait for.
>If you work things right, event check in the loop can be an if like
>if waitedfor&events: process_events(), which most of the time is a fast no-op).
>
>Then (without event stuff, and untested ;-) maybe something like:
>
> def loop(self):
> self_pool_popleft = self.pool.popleft
> self_pool_append = self.pool.append
> self_call_exit_funcs = self.call_exit_funcs
> try:
> while True:
> task = self_pool_popleft()
> if task.next() is None:
> self_call_exit_funcs(task)
^^^^^^^^^^^^^^^^^^^^^^ oops, need to switch if and else branches
> else:
> self_pool_append(task)
^^^^^^^^^^^^^^^^^^^^^^ oops, need to switch if and else branches
> except Indexerror:
> pass
>
>You could even consider putting the bound task.next methods in
>the deque instead of the task, and using deque rotation instead
>of popping and appending. Then, if you put the task.next's in
>reverse order in the deque to start with, self_pool[-1] will be
>the first, and self_pool_rotate() will bring the next into position.
>Which would make it look like (untested!):
>
> def loop(self):
> self_pool_pop = self.pool.pop
> self_call_exit_funcs = self.call_exit_funcs
> self_pool_rotate = self.pool.rotate
> try:
> while True:
> if self.pool[-1]() is None:
> self_call_exit_funcs(self_pool_pop())
^^^^^^^^^^^^^^^^^^^^^^ oops, need to switch if and else branches
> else:
> self_pool_rotate()
^^^^^^^^^^^^^^^^^^^^^^ oops, need to switch if and else branches
> except Indexerror:
> pass
>
>IWT if the pool remains unchanged most of the time, pool_rotate() ought
>to be faster than popping and appending. Note that exit_funcs will need
>a mapping of task.next -> task most likely, unless communication is
>entirely via a mutable task state object, and all that's needed is to
>me map to that and mess with exit state there and trigger a final .next()
>to wrap up the generator.
>
Sorry. I wonder what else I goofed up ;-)
Regards,
Bengt Richter
More information about the Python-list
mailing list