I'm losing two seconds per minute

BlitzMax Forums/BlitzMax Programming/I'm losing two seconds per minute

SpaceAce(Posted 2007) [#1]
I have a simple type with a goal field and an Update() method. The goal is set to some number, say 60,000, and the Update() method takes Delta_Time as an argument. In this case, Delta_Time is the number of milliseconds (via MilliSecs()) since the last call to Update(). Delta_Time is set immediately before Update() is called.

Update() simply adds Delta_Time to a cumulative total until that total reaches the goal. So, when 60,000 milliseconds worth of Delta_Time have been passed to Update(), the goal of 60,000 is reached. The problem is, I am losing just about two seconds per minute (60,000) of goal.

Where is this precision being lost? Is it in the number MilliSecs() returns or am I screwing something up along the way?

Edit: "Losing" is actually "gaining", I guess. The 60,000 goal is reached in about 58 seconds.

Thanks,
SpaceAce


tonyg(Posted 2007) [#2]
Have you a small code sample which shows the problem?


GfK(Posted 2007) [#3]
Its probably a rounding problem. Are you using Ints?

Use Floats/Doubles if you can.

Other than that, there may be some error in the way you're calculating your time. It does seem like a big margin of error to be a simple rounding issue.


H&K(Posted 2007) [#4]
Sometimes MilliSecs() is wrong. Normaly when the flip sync isnt a proper denominator of 1000 or the update call.
For example, if you tell the program to call Update every XMilli (say), then ask Update howmany milli has passed, it will say somtimes x-1milli. If you look your 2 seconds is 1/3 of a percent, so it might juts be a Hertz to milli convertion error (as GFK said), but I think there is something more fundementaly wrong with it.
There seems to be a small delay between when the system says a milli has passed, and when when the system counts that a milli has passed. (Tell the computer to call somthing every milli, and see how often it says no milli have passed between calls)

There is some code somewhere to count time in 1000th of a milli. And even if you get the same error that means you will only be 2milli out over 1mins. (and the way that looks like it works you shouldnt even get that)


ImaginaryHuman(Posted 2007) [#5]
When you call millisecs, it may be JUST about to increment to the next millisec value. If that is the case, and you read and use the value just prior to it changing, you're going to be about a millisec off. You can't really do delta time based on millisecs, it is not accurate enough. When you read the timer introduces up to 1 millisecond of error. If you want to know how much time has passed since the start, you should subtract the current millisecs from the start millisecs, and then subtract that from what the millisecs were at the previous `frame` minus the start millisecs. Ie must figure in the original start time in each delta calculation, not just base it relative to the previous frame.


SoggyP(Posted 2007) [#6]
Hello.

Would it be useful to put a balancing/normalising millisecs() count every x calls? Just a thought.

Goodbye.


SpaceAce(Posted 2007) [#7]
tonyg - I'll see if I can extract an example from the existing code. Right now, all the pieces of the project are interdependent due to residing in custom types and such. I'll throw together a simple Test.bmx when I get a chance.

GfK - I never used anything less precise than float. Just in case, I tried various other numerical variables but I always end with the same two seconds(ish) loss.

H&K - I did notice that when I put a "print" statement in the loop to show the time since last iteration, it tends to say "0, 0, 13, 0, 0, 11" and so on, along those lines. I thought that was odd.

AngelDaniel - I'm not sure I understand. I wrote some code based on what you said and got odd results (huge numbers). I tried changing the subtractions and got more odd results. I'm obviously just stupid (plus groggy from sleep) but I'm not getting it.

SoggyP - Do you mean in addition to the call that I make every frame?

SpaceAce


ImaginaryHuman(Posted 2007) [#8]
When you read the millisecs timer, the number of millisecs that it reports is not like `right in the middle of a millisecond` each time. `Time` itself is much finer grained and you might be reading the value just after the previous millisecond finished, or even just a nanosecond before it's about to change to a new value. You can be up to almost 1 millisecond `off`.

To correct it, you need to first store the millisecs that you read at the very start of the loop/app before you begin any delta stuff. You then need to subtract the current millisecs from that number. That gives you your delta time since time-recording began. Since it is relative to a `constant` beginning time, your measurements aren't going to be subject to the `rounding error` that the low-resolution timer is introducing. Then the next frame you do the same thing - subtract the overall start time from the current time. THEN you subtract this new amount from the previous frame's amount, to get the delta time. This way your delta time is more accurate, taking into account the distance between frames but also based on an `absolute` anchor beginning time rather than just relative. If you just measure relative to the previous frame, your measurement will be up to 1 millisecond off every frame, which will vary wildly depending on how much time your app spends each frame, and sharing the cpu etc.


SpaceAce(Posted 2007) [#9]
Thanks for trying to help but apparently I am a moron. Every implementation I try to write gives bizarre results. So far, the search function is not helping either.

This seems so damn simple, I must be missing something really obvious.

SpaceAce


REDi(Posted 2007) [#10]
Hi SpaceAce, I Knocked this up as a tester...
Graphics 800,600,0
Cls; Flip; Delay 250 ' allow the system to settle down

Local Test:TTest = New TTest
Local time:Int 
Local newtime:Int = MilliSecs()
Local starttime:Int = newtime
Repeat
	Cls
	time = newtime
	newtime = MilliSecs()
	If Test.Update(newtime-time) Then Print "Time taken = "+(newtime-starttime) ; End
	DrawText(Test.Current,10,10)
	Flip
Until KeyDown(KEY_ECAPE) Or AppTerminate()

Type TTest
	Field Goal:Int = 60000
	Field Current:Int
	Method Update(Delta:Int)
		Current:+Delta
		If Current=>Goal Then Return True
	EndMethod
EndType
Its seems to be correct to within a few milliseconds on my machine, the extra few millisecsonds would be due to the flip command as it waits to sync with the refresh rate (if the display is at 60hz then the variance could be as much as 16 millisecs)

Hope that helps.


SpaceAce(Posted 2007) [#11]
REDi, thank you, I will test that now. Your code seems to be using the same method I was originally, though, with none of the compensation that AngelDaniel mentioned. I'll see how it goes.

Edit: This is absolutely bizarre. I can't see a bit of difference between your code and mine but your code has not been off my a single millisecond in any of my tests. I am going to try inserting into the full application, now. Thank you.

SpaceAce


SpaceAce(Posted 2007) [#12]
Wow, the entire problem seems to be that I was counting the "progress" in a Float variable. I don't know if the float was adding .000234 to each call or what, but using an Int seems to solve the problem. That is very odd.

Changing the variable types in your test code above seems to have no effect but it changes everything in my program for some reason. I've been working on this for two days and the ONLY change I made in the end was to use an Int to count the progress, and that has fixed it.

SpaceAce


Grey Alien(Posted 2007) [#13]
seems weird that *reducing* precision should result in a fix. Maybe something esle is still wrong and the int is just covering it up?


Floyd(Posted 2007) [#14]
A Float is only 24-bit precision. An Int is 32-bit.

If you use Millisecs() values with float variables you will run into trouble when Millisecs() reaches 2^24.
This happens less than five hours after booting up.


Grey Alien(Posted 2007) [#15]
Then try doubles.


SoggyP(Posted 2007) [#16]
Hello.

Mine's a vodka.

Goodbye.


SpaceAce(Posted 2007) [#17]
For the record, I used Float, Double and Long, just for the hell of it. Int is the only one that works properly. However, changing the variable types in the small example posted by REDi seems to make no difference. That implies something else in my code but I hunted, hunted, hunted and isolated the Delta_Time and Update() methods as thrououghly as possible in my testing. I can't find anything else having an effect unless you count that fact that the whole thing is now wrapped in Grey Alien's framework (I am porting Magic Number over to the framework as a learning experiment).

Edit: Oh, also, I plugged REDi's code directly into the existing project and it gave me no problems then, either, so I don't really think it's something else in my code but I am still stumped as to why I needed to change my variables to Int but it didn't seem to matter with REDi's example.

SpaceAce


Grey Alien(Posted 2007) [#18]
Are you using the delta part of my framework to count to one minute as that should work?


SpaceAce(Posted 2007) [#19]
Grey Alien,
After rooting around in your code, I wasn't entirely sure that what I found was appropriate for incrementing my counter, so I threw my own delta code into the GameScreen's Logic() method.

SpaceAce


Grey Alien(Posted 2007) [#20]
I'll do a test which counts to 60 secs with my code and let you know.