tags:

views:

72

answers:

3

Hi,

Could someone please give me some help with limiting a loop to N iterations per minute in Python.

Lets say I have

limit = 5
for items in recvData:
     # > limit iterations in past minute? -> sleep for 60 seconds from last iterations before proceeding? #
     ... do work ...

How would I do the time check / sleeping to give the correct flow. I'm not worried about blocking the executing thread/process while it waits.

Thanks

A: 

Use grouper from the itertools recipes, combined with a time check.

import itertools, datetime, time
limit = 5

def grouper(n, iterable, fillvalue=None):
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.izip_longest(fillvalue=fillvalue, *args)

for items in grouper(limit, recvData):
    prev_minute = datetime.datetime.now().minute

    for item in items:
        # do stuff

    _, _, _, _, minute, second, _ = datetime.datetime.now()
    if minute == prev_minute:
        time.sleep( 60 - second )
katrielalex
Thanks, this fails with: TypeError: 'datetime.datetime' object is not iterable
Jason
@katrielalex: same as for AaronMcSmooth, you need to know `limit`, and know it very well :)
pszilard
@pszilard. katrialex took the same tack I did and executes all five at the beginning and _then_ calculates the remaining time to sleep.
aaronasterling
+1  A: 

It should be noted that this is not "hard real time" code. this will be off slightly because of OS scheduling and the like. That being said, unless you know that you need hard real time, this should suffice.

import time

limit = 5
starttime = time.time()
for i, item in enumerate(recvData):
    if not i + 1 % limit:
        sleeptime =starttime + 60 - time.time()
        if sleeptime > 0:
            time.sleep(sleeptime)
        starttime = time.time()
    #processing code
aaronasterling
Thanks, but if i put a 'print items' under processing code, the entire array is printed there an then, rather than printing 5 and then waiting 60 seconds before proceeding.
Jason
@Jason. You didn't say you wanted to print them in groups. This will process them one at a time as you requested. when you print the list out, _of course_ the whole array get's printed.
aaronasterling
@AaronMcSmooth: for this you need a correct estimate of `limit` s.t. `limit * iterationTime <= 1 min` *and* this has to be true for the entire execution.
pszilard
@AaronMcSmooth - But items is just one element of the recvData list per iteration? Hence each iteration one element will be printed, and there should be n iterations per minute. Sorry if im confused.
Jason
@pszilard. I specifically constructed it so that I do not. It performs the five iterations and __then__ calculates the remaining time to sleep. The iteration time isn't required to calculate this. I need that estimate __iff__ i want the five iterations evenly distributed throughout that one minute interval which is another thing OP didn't specify.
aaronasterling
@Jason. that was I typo, I've renamed it to `item`. there will be n iterations per minute (approximately) and there will be one item processed per iteration.
aaronasterling
@AaronMcSmooth: correct... if the iteration time is not a random value in the [0.01, 10] seconds interval. If the loop contains e.g. network-related stuff with highly varying i/o latency you do need a bit more fancy scheme... ;)
pszilard
@pszilard. If five iterations overflow the one minute boundary, then it will reset the `starttime` and begin a new set of five iterations immediately. er...after I fix a quick bug that is.
aaronasterling
A: 

This very much depends on the type of work you're doing inside the loop and on how accurate you want this mechanism to work.

ATM I can come up with 2 possible schemes:

  • If one iteration takes about constant time than you could calculate an average from the first few iterations and "sleep" for 1 - iterationTime afterwards.
  • Otherwise, you can poll the time and recalculate the average every step (or a few steps).

Depending on the standard deviation of your single loop execution times both scheme can work quite well, but if the execution times are very varying, neither of them will. Also, if you want evenly distributed loop cycles and not only keep the average/min you have to distribute the sleep-s and do one after each iteration.

I am not familiar enough with Python to know how expensive is to query the time and what other Python-specific issues might pop up with sleep-ing, though.

pszilard