UX improvement fix for the common case: import time class Timer(object): def __init__(self, seconds): self.seconds = seconds self.restart() def restart(self): self.end = time.time() + self.seconds @property def expired(self): return (time.time() > self.end) Example: class FPS(object): def __init__(self): self.counter = 0 self.timer = Timer(1) def process(self): self.counter += 1 if self.timer.expired: print "FPS: %s" % self.counter self.counter = 0 self.timer.restart() All code above is in public domain. -- anatoly t.
On Wed, Feb 26, 2014 at 4:24 PM, anatoly techtonik
UX improvement fix for the common case:
import time
class Timer(object): def __init__(self, seconds): self.seconds = seconds self.restart()
def restart(self): self.end = time.time() + self.seconds
@property def expired(self): return (time.time() > self.end)
You just showed how easy it is to implement it manually :) Your example, though, is a really bad idea:
class FPS(object): def __init__(self): self.counter = 0 self.timer = Timer(1) def process(self): self.counter += 1 if self.timer.expired: print "FPS: %s" % self.counter self.counter = 0 self.timer.restart()
Implication is that you call process() many times a second, right? Well, querying the current system time is often quite expensive, so this could seriously damage your FPS; and more importantly, it's inaccurate. Let's suppose you're actually achieving 2.5 FPS - that is to say, you call process() every 400ms. Here's how it'll go: Time 400: not expired Time 800: not expired Time 1200: expired, reset time to zero Time 1600: timer says 400, not expired Time 2000: timer says 800, not expired Time 2400: timer says 1200, expired, reset time to zero You're going to say 3 FPS, when it's really 2.5. For measuring FPS, you should probably use an algorithm more like this: 1) Spawn an FPS counter thread, or async alarm, or something, to be executed every second. 2) Each frame, increment a counter. Never reset the counter. 3) Every time the alarm goes off, record the current time and the current frame counter. 4) Your FPS is (frames - last_frames) / (time - last_time) That way, you query the current time roughly once a second. It's safe against sleep(1) taking more than one second to return (because it actually queries the time), it's safe against the FPS being extremely low, and it's fairly efficient. Plus, it doesn't require any code in the time module :) ChrisA
On Wed, Feb 26, 2014 at 11:29 AM, Chris Angelico
On Wed, Feb 26, 2014 at 4:24 PM, anatoly techtonik
wrote: UX improvement fix for the common case:
import time
class Timer(object): def __init__(self, seconds): self.seconds = seconds self.restart()
def restart(self): self.end = time.time() + self.seconds
@property def expired(self): return (time.time() > self.end)
You just showed how easy it is to implement it manually :)
Yes, but the frequency of this element made it a better position than other datetime functions.
Your example, though, is a really bad idea:
class FPS(object): def __init__(self): self.counter = 0 self.timer = Timer(1) def process(self): self.counter += 1 if self.timer.expired: print "FPS: %s" % self.counter self.counter = 0 self.timer.restart()
Implication is that you call process() many times a second, right? Well, querying the current system time is often quite expensive, so this could seriously damage your FPS; and more importantly, it's inaccurate. Let's suppose you're actually achieving 2.5 FPS - that is to say, you call process() every 400ms. Here's how it'll go:
Time 400: not expired Time 800: not expired Time 1200: expired, reset time to zero Time 1600: timer says 400, not expired Time 2000: timer says 800, not expired Time 2400: timer says 1200, expired, reset time to zero
You're going to say 3 FPS, when it's really 2.5.
+/- 1 FPS is acceptable error as long as FPS doesn't fall below 60. For my script that in PySDL2 is about 1000 for filling a small window with lines.
For measuring FPS, you should probably use an algorithm more like this:
1) Spawn an FPS counter thread, or async alarm, or something, to be executed every second.
I don't want to mess with woes of concurrent programming for this kind of accuracy. Python is good at what it is good for. https://stackoverflow.com/questions/134867/which-programming-language-makes-...
2) Each frame, increment a counter. Never reset the counter.
Although offtopic, but I don't get why resetting a counter is worse than storing two values and subtracting them.
3) Every time the alarm goes off, record the current time and the current frame counter. 4) Your FPS is (frames - last_frames) / (time - last_time)
That way, you query the current time roughly once a second. It's safe against sleep(1) taking more than one second to return (because it actually queries the time), it's safe against the FPS being extremely low, and it's fairly efficient.
It increases complexity, but doesn't increase accuracy, and therefore is fairly inefficient for maintenance. In formula: (frames - last_frames) / (time - last_time) (time - last_time) == 1 so where I get 3 frames, your code will get 2. The code only makes sense when timer fire is delayed (I don't know if it is the case with threads) or when you make frame-based FPS measurements (every 10 frames or so).
Plus, it doesn't require any code in the time module :)
FPS is only one problem that timer solves. https://www.google.by/search?q=timer - even Google calculator has one.
Yes, this is OT, but quite a bit of it is on the general subject of
threading and counters and timing, all of which are important to
Python. But if you want to continue this discussion further, we should
probably move to python-list or something.
On Thu, Feb 27, 2014 at 4:37 AM, anatoly techtonik
On Wed, Feb 26, 2014 at 11:29 AM, Chris Angelico
wrote: For measuring FPS, you should probably use an algorithm more like this:
1) Spawn an FPS counter thread, or async alarm, or something, to be executed every second.
I don't want to mess with woes of concurrent programming for this kind of accuracy. Python is good at what it is good for. https://stackoverflow.com/questions/134867/which-programming-language-makes-...
Threads are pretty easy. Remember, you're working with an idle thread - it wakes up once a second to do stuff, then goes back to sleep. That's really easy to handle: a simple while loop in a separate function.
2) Each frame, increment a counter. Never reset the counter.
Although offtopic, but I don't get why resetting a counter is worse than storing two values and subtracting them.
Technically, it's possible for this operation to be broken in half: frame_count += 1 If the other thread comes in between the read and the write, your next second will have twice the apparent FPS. By restricting one thread to read-only access, you guarantee atomicity. It's an obscure situation, but just get into the habit of having everything written to by one thread only, and threading is really easy.
3) Every time the alarm goes off, record the current time and the current frame counter. 4) Your FPS is (frames - last_frames) / (time - last_time)
That way, you query the current time roughly once a second. It's safe against sleep(1) taking more than one second to return (because it actually queries the time), it's safe against the FPS being extremely low, and it's fairly efficient.
It increases complexity, but doesn't increase accuracy, and therefore is fairly inefficient for maintenance. In formula:
(frames - last_frames) / (time - last_time)
(time - last_time) == 1
Wrong. In the first place, time.time() returns a float, so that would be 1.0 (which means you'll get a float result, even in Py2); and in the second place, sleeping for one second is guaranteed to pause for at least one second, maybe more, and there's other code around it too. So usually, you'll have a figure higher than 1.0. This is even more pronounced if your loop waits for some other action, like the painting of the next frame.
so where I get 3 frames, your code will get 2. The code only makes sense when timer fire is delayed (I don't know if it is the case with threads) or when you make frame-based FPS measurements (every 10 frames or so).
Timer fire should always be expected to be delayed, unless you're running on some sort of real-time system. (And even then, I don't know how you get around the laws of physics. Maybe it could throw an exception if the sleep lasted too long.) It may not seem significant when you're expecting 60 FPS, but two points. 1) FPS displays are really important when something goes wrong and you're achieving 2.5 FPS. And I have seen times when this exact thing has happened - in fact, I've watched a game struggle through <1.0 FPS, and its display actually couldn't handle it, which means I have no idea how poorly it was really performing. 2) Running at exactly 60 FPS when your screen repaints at exactly 60 FPS is good. Running at 59 FPS when your screen repaints at 60 FPS is bad. Look up Vsync and why so many games let you turn it on or off. Misdisplaying the FPS by one could have someone toggle Vsync inappropriately, or complain that it's not working. Showing 59.99 FPS doesn't look too bad (and you could have your display round that carefully). ChrisA
On 2/26/2014 12:37 PM, anatoly techtonik wrote:
On Wed, Feb 26, 2014 at 11:29 AM, Chris Angelico
wrote:
For measuring FPS, you should probably use an algorithm more like this:
1) Spawn an FPS counter thread, or async alarm, or something, to be executed every second.
I don't want to mess with woes of concurrent programming for this kind of accuracy. Python is good at what it is good for.
What 'Python is good at' changes when new features and modules are added. The new asynch module is about 'non-blocking' programming. Its sleep task makes having a process periodically wake up, do something, and go back to sleep simple. We may assume that async.sleep(n) uses whatever non-blocking watchdog timer system call is available in the major OSes. There is not much reason left to program one in Python.
https://stackoverflow.com/questions/134867/which-programming-language-makes-...
The first answer is to use a functional language because values are never changed. For this use, the FPS reporter should access an 'anti-functional' changing counter. Of course, the 'counter' is not actually changed (as it would be if it were a named memory slot in C). Rather than name 'counter' is rebound to another int. In any case, there worry is that is two threads or threadlets rebind the name, one or the other will get discombobulated. That is not the case here.
2) Each frame, increment a counter. Never reset the counter.
Although offtopic, but I don't get why resetting a counter is worse than storing two values and subtracting them.
Since the frame function that increments the counter does not care if it is reset to 0, I might have the FPS reported reset it. Either way is a minor detail. -- Terry Jan Reedy
On Thu, Feb 27, 2014 at 5:21 AM, Terry Reedy
Since the frame function that increments the counter does not care if it is reset to 0, I might have the FPS reported reset it. Either way is a minor detail.
Only if you have an atomic increment operation, which I don't believe Python exposes. But yes, that's a minor detail. I actually hadn't thought through all that when I first said it - it was just a habit of "don't write to anything from two threads unless you really have to". ChrisA
On 26/02/2014 05:24, anatoly techtonik wrote:
UX improvement fix for the common case:
An unexploded improvement fix, now there's a novelty :) -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence --- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
From: anatoly techtonik
UX improvement fix for the common case:
import time
class Timer(object): def __init__(self, seconds): self.seconds = seconds self.restart()
def restart(self): self.end = time.time() + self.seconds
@property def expired(self): return (time.time() > self.end)
time.time() is almost always the wrong thing to use for almost anything. It's a float, it doesn't guarantee precision better than 1 second, it can be slow (on the order of milliseconds), and it doesn't guarantee monotonicity. You probably wanted monotonic here, although without knowing your use case, it's possible you wanted monotonic_raw with a fallback to monotonic, or perf_counter. Also, this kind of timer is only really appropriate for a frame-loop app (as in a traditional arcade game or digital audio app), where you're inherently only checking, say, once every 20ms when idle and less often when busy. But any such loop should be sharing a _single_ call to the clock for the entire loop instance, not reading the clock repeatedly. Plus, most such programs in Python are probably written in something like pygame, which already provides a nicely-integrated timer (and many of the rest probably should be…). In an event loop, or a program based on threads or coroutines, or anything other than a frame loop, this is the wrong thing to do.
participants (5)
-
anatoly techtonik
-
Andrew Barnert
-
Chris Angelico
-
Mark Lawrence
-
Terry Reedy