1

The code below is pretty generic. In it's final state, it will run some operations (such as sampling data from a serial stream), but my goal right now is to optimize the looping structure to run as fast as possible.

Currently, I am getting a max frequency of ~100-140 before I trip the if statement.

Is there a more efficient way to run this?

NOTE: I know the while loop is essentially empty. Of course the limiting factor of how fast it will run will be the function that I call inside of it. What I'm trying to ensure is that the code in the while loop is running as efficiently as possible

import time
frequency = float(raw_input("enter sampling frequency in Hz: "))

zero_time=time.time()
i=0

try:
    while True:
        sample_start_time=time.time()
        print 'sample',i, 'taken at', sample_start_time-zero_time
        i+=1
        sample_end_time=time.time()
        if 1/frequency-(sample_end_time-sample_start_time)<0:
            print "sampling frequency is too large...closing"
            break
        time.sleep(1/frequency-(sample_end_time-sample_start_time))
except KeyboardInterrupt:
    pass
Chris
  • 9,603
  • 15
  • 46
  • 67
  • 8
    Pro-tip: use [`timeit.default_timer()`](http://docs.python.org/2/library/timeit.html#timeit.default_timer) instead of `time.time()` to get the best timer resolution on any platform. – Martijn Pieters Nov 10 '12 at 19:51
  • 2
    Why are you timing a print statement? – Eric Nov 10 '12 at 19:52
  • `print` blocks on the tty, so you really don't want to be timing it. – Nick Bastin Nov 10 '12 at 19:54
  • @Eric In production there would be some more code between sample_start_time and sample_end_time. I'm just trying to optimize the while loop itself first before I get there. – Chris Nov 10 '12 at 19:54
  • @Chris: Measuring the time taken to print to the output is a useless way to "optimize the while loop" – Eric Nov 10 '12 at 19:55
  • 3
    It seems hard to "optimize" an otherwise effectively empty while .. [likely] insignificant time *might* be saved if using an local variable binding for the calculation (it would look nicer anyway). Python is not designed for RT operations nor does it generally run on RT operating systems .. and this also affects `time.sleep` (e.g. see http://stackoverflow.com/questions/7273474/behavior-of-pythons-time-sleep0-under-linux-does-it-cause-a-context-switch) .. just keep in mind. –  Nov 10 '12 at 19:56
  • It would be more efficient to use an asynchronous event loop and a timer. – Keith Nov 10 '12 at 19:58
  • 5
    Is it only me, or does he really ask how to optimize the while of a "while: do_nothing" loop? – Don Question Nov 10 '12 at 20:01
  • The loop is as efficent is it can get, however try do the process in multiprocessing mode or in parallel processing. –  Nov 10 '12 at 20:24

3 Answers3

4

As the comments say, the question is somewhat meaningless with an empty loop, but here are a few things that might help (ordered roughly by increasing importance).

  1. Remove the print statement from the loop. This is probably the most expensive thing you're doing currently.
  2. Investigate alternative timers. time.time and time.sleep may not offer the best performance, and the timeit module has some alternatives.
  3. Calculate the period of your timer ahead of time, rather than doing 1/frequency repeatedly.
  4. Calculate the difference between times (and between that difference and the period) just once, rather than twice per loop.

Even if you don't want to test your actual work case, you might want to put a call to an empty function in the loop, so you can see how much the function call overhead costs. It is likely to take more time than 3 and 4 in my list above.

Blckknght
  • 100,903
  • 11
  • 120
  • 169
3

Yes, your "while loop" is running as efficiently as possible. You cannot write a more efficient while loop than:

while True:
    # doStuff
Eric
  • 95,302
  • 53
  • 242
  • 374
  • 1
    Agreed. The source of the slowdown is likely in the called function, not in the structure of the loop. Additional speed can be obtained by caching attribute lookups, but a couple attribute lookups don't take ~10ms to complete. – user4815162342 Nov 10 '12 at 20:15
  • Well, I'd think implementing the while loop in C would be unnoticeably more efficient *nitpicking* ;) – Voo Nov 10 '12 at 20:27
  • 1
    No, his while loop contains a number of system calls. You can reduce the number of system calls. I think the OP is talking about absolute time, not efficiency. – Keith Nov 10 '12 at 20:37
3
from time import time, sleep
frequency = float(raw_input("enter sampling frequency in Hz: "))
target_freq = 1/frequency

try:
    sampling_rate = target_freq
    while sampling_rate >= target_freq:
        sample_start_time = time()
        # maybe do some stuff here
        sample_end_time = time()
        time_diff = sample_end_time - sample_start_time
        if time_diff < target_freq:
            sleep(target_freq - time_diff)
except KeyboardInterrupt:
    pass

Have a look at the Python performance tips page, especially the section on avoiding dots.

Matti Lyra
  • 12,828
  • 8
  • 49
  • 67