Page 1 of 2 12 LastLast
Results 1 to 10 of 13

Thread: Discussion: Time base for TBEM

  1. #1
    thinBasic author ErosOlmi's Avatar
    Join Date
    Sep 2004
    Location
    Milan - Italy
    Age
    50
    Posts
    8,198
    Blog Entries
    2
    Rep Power
    10

    Discussion: Time base for TBEM

    Yes it is working fine. If you have set default initial start time at GetTickCount it will work and can be considered a solution for common situations.

    Possibly send me again sources, I would like to study it with more time during the week-end.
    At the moment I have no a precise idea of what could be better.

    Maybe a possible situation is not relay on GetTickCount alone but determine a sort of initial time when module is loaded by each script and than use that time as TIME ZERO. All the rest will be based on that ZERO time. But to be honest I'm just guessing a possible solution without any test.
    Better to talk about that in TBEM dedicated forum.

    Anyhow, for the purpose of this and other scripts, new version solve the problem.

    Ciao
    Eros

    www.thinbasic.com | www.thinbasic.com/community/ | psch.thinbasic.com
    Win10Pro 64bit - 8GB Ram - Intel i7 M620 2.67GHz - NVIDIA Quadro FX1800M 1GB

  2. #2

    Discussion: Time base for TBEM

    Win 98 (Gettickcount) is only accurate to 0.05 (The number may be 1.234, but the next number will be 1.274.) (That is 20FPS, if you hit every cycle, and don't pass one. Windows skips calls for gettickcount if you call it too often. Just sends back old buffered data, before it can give you back the real time.)

    Win XP is 0.016 per tick (That is 60FPS)

    On both, the timer rolls-over after a few days... producing a negative value as the output.
    (-Neg - -Neg = -Neg) as opposed to (+Pos - +Pos = +Pos). (New - Old = Diff)
    When negative... Old = -4, New = -3, -2, -1, 0, 1, 2, 3...
    (-3 - -4 = -1) as opposed to (+3 - +2 = +1)

    Use the HighResolutionTimer, it is more accurate to the time, and takes less time for a call.

    TimeGetTime is actually better for all computers. It rolls-over but is faster.

    The best method would be to do this...
    TimeGetTime... and HighResolutionTimer...
    Wait for about one minute... Get them again...

    Now you can compare real "Time" to the frequency-timer... and adjust instantly. (Keep using HRT for all next calls.)

    But less critical things would use TimeGetTime, and adjust for the predictable negative value, as GetTickCount.

  3. #3
    thinBasic MVPs Michael Hartlef's Avatar
    Join Date
    Sep 2006
    Location
    Leverkusen, Germany
    Age
    51
    Posts
    3,281
    Blog Entries
    2
    Rep Power
    340

    Re: Discussion: Time base for TBEM

    Here is a test that I made for how long 500.000 calls of a time function take. I think it isn't something that is important in our case.

    [code=thinbasic]' Empty ThinBASIC CONSOLE file template
    uses "console"

    dim t,t2,t3, st,et,dt as dword
    dim i as long
    dim length as long value 500000

    Declare Function timeGetTime Lib "winmm.dll" Alias "timeGetTime" () As DWord
    Declare Function GetTickCount2 Lib "kernel32.dll" Alias "GetTickCount" () As DWord

    'Check how long Gettickcount via thinBasic call takes
    st = Gettickcount
    for i = 1 to length
    dt = gettickcount
    next
    et = gettickcount
    t = et - st

    'Now check how long Gettickcount via API call takes
    st = Gettickcount
    for i = 1 to length
    dt = gettickcount2
    next
    et = gettickcount
    t2 = et - st

    'Now check how long timeGettime takes
    st = Gettickcount
    for i = 1 to length
    dt = timeGetTime
    next
    et = Gettickcount
    t3 = et - st

    console_writeline("GetTickCount(TB): " + t + " GetTickCount(API): " + t2 + " timeGetTime: " + t3)
    console_waitkey

    [/code]
    Running Windows 7 Home, 64 bit, 8 GB ram, Athlon II X2 255, ATI Radeon HD 4200.

  4. #4
    thinBasic MVPs Michael Hartlef's Avatar
    Join Date
    Sep 2006
    Location
    Leverkusen, Germany
    Age
    51
    Posts
    3,281
    Blog Entries
    2
    Rep Power
    340

    Re: Discussion: Time base for TBEM

    The API call for GetTickCount is slightly FASTER then timeGetTime.
    Running Windows 7 Home, 64 bit, 8 GB ram, Athlon II X2 255, ATI Radeon HD 4200.

  5. #5
    thinBasic author ErosOlmi's Avatar
    Join Date
    Sep 2004
    Location
    Milan - Italy
    Age
    50
    Posts
    8,198
    Blog Entries
    2
    Rep Power
    10

    Re: Discussion: Time base for TBEM

    Michael,

    I think your latest version already solved all standard situations like games or other thinBasic scripts I can think about. So thanks for it.

    The only possible open problem is when TBEM is used on "long run" scripts on servers or other machines.
    You can easily solve the problem stating in help file that this module is not developed for server side scripts or for scripts running for more than xx days.

    When you will have more time or worth to dedicate time to it, maybe you will find a better solution but for the moment it is a perfect and very valuable module.
    In the meantime if my brain will think about a possible implementation I will ask here.

    Thanks a lot
    Eros
    www.thinbasic.com | www.thinbasic.com/community/ | psch.thinbasic.com
    Win10Pro 64bit - 8GB Ram - Intel i7 M620 2.67GHz - NVIDIA Quadro FX1800M 1GB

  6. #6

    Re: Discussion: Time base for TBEM

    For long scripts, where time is to extend beyond a set period... (Timer expires)... the time calibration to real system time, should be used as the base... Updated at intervals beyond a minute. (Precision timers are only good for short times. Once time exceded a minute, value reset back to 0, and a time-stamp value is used for a compare.)

    val = TimeStamp
    val = PrecisionStart
    Loop
    TimerCompare (If PrecisionStart>PrecisionLimit THEN UseTimeStamp ELSE UsePrecisionTime)
    End Loop

    When they have exceded a minute, or hour... there is no need to know that 8574345.12345678 seconds passed. Knowing that 8574345 seconds passed is all that is needed.

    Only each element that requires precision, should have that value used. (Those should not take longer than an hour.)

    Here is the OVERHEAD values... of each time event.

    Summary... Being FAST and not-precise, is pure OVERHEAD.

    Are we there yet? Are we there yet? Are we there yet? Are we there yet? Where are we going? (I told you 15 times already, we are still in the driveway! Now buckle-up!)

    [code=thinbasic]' Empty ThinBASIC CONSOLE file template
    Uses "UI"

    HiResTimer_Init

    GLOBAL xHRT, xHRT_Old, xHRT_New AS QUAD
    GLOBAL xt1,xt2,xt3,xt4, t4 AS QUAD
    GLOBAL c1,c2,c3,c4, u1,u2,u3,u4 AS DOUBLE

    GLOBAL t1,t2,t3, st,et,dt AS DWORD

    GLOBAL i as long
    GLOBAL length as long value 1000000

    Declare Function timeGetTime Lib "winmm.dll" Alias "timeGetTime" () As DWord
    Declare Function GetTickCount2 Lib "kernel32.dll" Alias "GetTickCount" () As DWord

    FUNCTION TBMAIN()
    LOCAL hDlg AS DWORD

    DIALOG New 0, "FINISHED",-1,-1, 160, 120, _
    %WS_POPUP Or %WS_VISIBLE Or _
    %WS_CLIPCHILDREN Or %WS_CAPTION OR _
    %WS_SYSMENU Or %WS_MINIMIZEBOX, 0 To hDlg

    DIALOG SHOW MODAL hDlg, CALL dlgProc

    END FUNCTION

    ' -- Callback for dialog
    CALLBACK FUNCTION dlgProc()

    ' -- Test for messages
    SELECT CASE CBMSG

    CASE %WM_INITDIALOG

    ' -- ##### Getting a unique change value (How precise it is)

    st = Gettickcount
    DO UNTIL gettickcount <> st
    xHRT = gettickcount
    LOOP
    et = gettickcount
    t1 = et - st


    st = Gettickcount2
    DO UNTIL gettickcount2 <> st
    xHRT = gettickcount2
    LOOP
    et = gettickcount2
    t2 = et - st


    st = timeGetTime
    DO UNTIL timeGetTime <> st
    xHRT = timeGetTime
    LOOP
    et = timeGetTime
    t3 = et - st


    xHRT_Old = HiResTimer_Get
    DO UNTIL HiResTimer_Get <> xHRT
    xHRT = HiResTimer_Get
    LOOP
    xHRT_New = HiResTimer_Get
    t4 = xHRT_New - xHRT_Old

    MSGBOX 0, "How ACURATE each call is." & CRLF _
    & CRLF & "GetTickCount(TB): " & FORMAT$(t1/1000, "#0.000000") _
    & CRLF & "GetTickCount(API): " & FORMAT$(t2/1000, "#0.000000") _
    & CRLF & "TimeGetTime(API): " & FORMAT$(t3/1000, "#0.000000") _
    & CRLF & "HRT CALL: " & FORMAT$(t4/1000000, "#0.000000")

    ' -- ##### Based on testing... A dead call is one where no change has taken place...
    'Determine OVERHEAD by how many useless calls are sent, returning values without change...
    u1 = (t1/1000)
    u2 = (t2/1000)
    u3 = (t3/1000)
    u4 = (t4/1000000)

    ' -- ##### Testing time to execute a CALL
    'Check how long Gettickcount via thinBasic call takes

    xHRT = 0 ' Ensures value is with data, and all are called with same VAR delay

    xHRT_Old = HiResTimer_Get
    st = Gettickcount
    for i = 1 to length
    xHRT = gettickcount
    next
    xHRT_New = HiResTimer_Get
    et = gettickcount
    t1 = et - st
    xt1 = xHRT_New - xHRT_Old

    'Now check how long Gettickcount via API call takes
    xHRT_Old = HiResTimer_Get
    st = Gettickcount2
    for i = 1 to length
    xHRT = gettickcount2
    next
    xHRT_New = HiResTimer_Get
    et = gettickcount2
    t2 = et - st
    xt2 = xHRT_New - xHRT_Old

    'Now check how long timeGettime takes
    xHRT_Old = HiResTimer_Get
    st = timeGetTime
    for i = 1 to length
    xHRT = timeGetTime
    next
    xHRT_New = HiResTimer_Get
    et = timeGetTime
    t3 = et - st
    xt3 = xHRT_New - xHRT_Old

    ' HRT
    xHRT_Old = HiResTimer_Get
    for i = 1 to length
    xHRT = HiResTimer_Get
    next
    xHRT_New = HiResTimer_Get
    xt4 = xHRT_New - xHRT_Old

    MSGBOX 0, "How FAST 1,000,000 calls are made." & CRLF & "(Second value is HRT true value.)" & CRLF _
    & CRLF & "GetTickCount(TB): " & FORMAT$(t1/1000, "#0.000000") & " : " & FORMAT$(xt1/1000000, "#0.000000") _
    & CRLF & "GetTickCount(API): " & FORMAT$(t2/1000, "#0.000000") & " : " & FORMAT$(xt2/1000000, "#0.000000") _
    & CRLF & "TimeGetTime(API): " & FORMAT$(t3/1000, "#0.000000") & " : " & FORMAT$(xt3/1000000, "#0.000000") _
    & CRLF & "HRT CALL: " & FORMAT$(xt4/1000000, "#0.000000")

    c1 = (1/(xt1/1000000)*1000000)
    c2 = (1/(xt2/1000000)*1000000)
    c3 = (1/(xt3/1000000)*1000000)
    c4 = (1/(xt4/1000000)*1000000)

    MSGBOX 0, "How MANY calls made in a second." & CRLF _
    & CRLF & "GetTickCount(TB): " & FORMAT$(c1, "#0") _
    & CRLF & "GetTickCount(API): " & FORMAT$(c2, "#0") _
    & CRLF & "TimeGetTime(API): " & FORMAT$(c3, "#0") _
    & CRLF & "HRT CALL: " & FORMAT$(c4, "#0")

    ' Calculate OVERHEAD of calling X calls per second...
    DIM Count1, Count2 AS LONG
    xHRT_Old = HiResTimer_Get
    xHRT_New = (xHRT_Old + 1000000)
    st = Gettickcount
    DO UNTIL HiResTimer_Get > xHRT_New
    Count1 += 1
    dt = Gettickcount
    IF st <> dt THEN
    Count2 += 1
    st = dt
    END IF
    LOOP
    xHRT_New = HiResTimer_Get
    xt4 = xHRT_New - xHRT_Old
    MSGBOX 0, "Actual wasted Calls (GetTickCount-TB)..." & CRLF & "Seconds: " & FORMAT$(xt4/1000000, "#0.000000") & ", Total: " & Count1 & ", Changed: " & Count2 & " " _
    & CRLF & "Percent of good calls: %" & FORMAT$((100/Count1)*Count2, "#0.000000")

    Count1 = 0
    Count2 = 0
    xHRT_Old = HiResTimer_Get
    xHRT_New = (xHRT_Old + 1000000)
    xt1 = HiResTimer_Get
    DO UNTIL HiResTimer_Get > xHRT_New
    Count1 += 1
    xt2 = HiResTimer_Get
    IF xt1 <> xt2 THEN
    Count2 += 1
    xt1 = xt2
    END IF
    LOOP
    xHRT_New = HiResTimer_Get
    xt4 = xHRT_New - xHRT_Old
    MSGBOX 0, "Actual wasted Calls (HighResTimer-TB)..." & CRLF & "Seconds: " & FORMAT$(xt4/1000000, "#0.000000") & ", Total: " & Count1 & ", Changed: " & Count2 & " " _
    & CRLF & "Percent of good calls: %" & FORMAT$((100/Count1)*Count2, "#0.000000")

    MSGBOX 0, "Result... Being a FAST reply, with NO CHANGE, is HIGH OVERHEAD." & CRLF & "You are calculating nothing fast, slowing down the whole PC."

    CASE %WM_CLOSE
    ' -- Put code to be executed before dialog end here

    END SELECT

    END FUNCTION
    [/code]

  7. #7

    Re: Discussion: Time base for TBEM

    The relation to games... as a timer...

    The highest resolution would be 0.016 seconds, or roughly 60FPS (60 call events per second.) Actual is less...

    For instance, if you start at 0.000000001 second...
    The next time event "Could" be 0.000000001 second away (0.000000002) or it Could be 0.016000001 (0.016000002)...

    You have no idea what the actual "Start" time is, since you can only see changes of 0.016... When you execute a single call, you also have to add that delay. (That is what you see between the API and the TB variation. API "is" faster, but it "is" even faster for the core, than trying to pass an API call from the code, to the core, to the DLL, and get a reply.)

    EG, if used as a frame-release trigger... (Holding back until any change is detected.) The fastest speed you would ever see is 60FPS. (But that is if you notice every change, as it happens. What happens in reality... the actual change is delayed, because you are sending thousands of calls, and the one with the change is delayed by the overhead.)

    If used as an animation trigger... (Activating an event at an interval.) If you use the START, you will only be behind by about 0.016... If used as a continuance... You will miss events, and sense double events. (Time passed = none, or 1,1,2,1,1,0,2,1,1,2... as times overlap, and don't match with the refresh rate. Creates a jittery motion or sound.)

    If the program on the other side, which called the routine, is waiting for an event... that would create delays in the program they are running. (One that will drive them mad, trying to figure out why they cant get over 60 FPS, and all lower FPS jumps around for no apparent reason.)

    You COULD...

    Create two internal runs... (One or the other would be used.)

    If I say an event should happen every 0.001 s, you use the HighTimer.
    If I say an event should happen every 0.1 s, you use the GetTick.
    If I say an event should happen every 1.0 s or more, you use WindowsTime.

    For every event type, there could be two styles of return...
    ReleaseStyle (Uses one start value, tracks time from start as trigger.)
    SequenceStyle (Resets like a game-timer at every call.)

    For those who like them, you could also create a buffered version.
    When I ask for an event-check, you return how many events occured since last time I asked. (If I am busy, and don't want to miss events that can't be handled at a designated "Fire", where they would be out of sequence, and also interfere with code that needs priority processing.) This would be like a "Sleep" command to you, asking you for a pause and to buffer events while paused from you.

    Release style would initialize with the start value, and trigger every X from that start value. (Best for FPS games. Values will always be x/60 from start time. Though not all 60 may be there, the "Timing" will always match. 4:00:00.0001, 4:00:00.0002, {missed} 4:00:00.0005, 4:00:00.0006. This is what video depends on... time. Four events happen, but I see that I am missing 2... and that 0.0002 is added {in time}, to the event that follows.)

    Sequence style would reset, and tell you passed time, good if you buffer moves, and don't depend on time-events to be linear and smooth. (Values from call to call would not always be 1/60th of a second, though you ask them to be. 4:00:00.0001, 4:00:00.0167, 4:00:00.0532, 4:00:00.1180... resulting in passed time lost, but four events. You can't even begin to estimate if you lost time less than 1/60 of a second.)

    This is also the same problem I am having with the animations using game-time as a time-passed control. The slower the frame-rate, the more inaccurate it becomes. In the end, the "Time * x" should equal "Timer + Timer + Timer * x", but the timer looses precision, misses calls, and X is never added into that time-segment, thus, the animation falls short and out of sequence. Seeming to get slower and slower.

    Use the event to drive a clock... show that "Driven" time, side by side with real time, every 30 seconds just save both values. Record the diference, and you can watch it drift on any trigger that is not timed from "Start", and is not "HighResolution".

    You can also run the timer-code through an "Extended loop"... to see some of the issues...

    [code=thinbasic]' Empty GUI script created on 10-09-2008 22:53:18 by (ThinAIR)

    USES "TBGL"

    Declare Function TimeGetTime Lib "winmm.dll" Alias "timeGetTime" () As DWORD
    Declare Function GetTickCount2 Lib "kernel32.dll" Alias "GetTickCount" () As DWORD

    DIM cDiff(200), c, cLength AS LONG
    DIM a, b, c1, c2, c3, cOld_Diff, cNew_Diff AS DWORD

    cLength = 1000000

    ' -- Will not work on HighResTimer... all values change, so it is pointless checking and recording the diff.

    'a = TBGL_GetFrameRate
    a = GetTickCount
    'a = GetTickCount2
    'a = TimeGetTime
    FOR c = 1 TO cLength
    'b = TBGL_GetFrameRate
    b = GetTickCount
    'b = GetTickCount2
    'b = TimeGetTime
    IF a <> b THEN
    c2 += 1
    cNew_Diff = b - a
    cDiff(c2) = cNew_Diff
    IF cOld_Diff <> cNew_Diff THEN
    c3 += 1
    cOld_Diff = cNew_Diff
    END IF
    ELSE
    c1 += 1
    END IF
    a = b
    NEXT
    MSGBOX 0, "Other_Timer test results" & CRLF & _
    "Total calls: " & cLength & CRLF & _
    "No changes: " & c1 & CRLF & _
    "Total changes: " & c2 & CRLF & _
    "Diff err: " & c3

    MSGBOX 0, "Each value change: " & JOIN$(cDiff, ", ")

    ' -- This shows some interesting things...

    DIM zDiff(300), zLength AS LONG
    DIM x, y, z, z1, z2, z3, zOld_Diff, zNew_Diff AS LONG

    zLength = 300

    x = TBGL_GetFrameRate
    FOR z = 1 TO zLength
    y = TBGL_GetFrameRate
    zDiff(z) = Y
    IF x <> y THEN
    z2 += 1
    IF zOld_Diff <> Y THEN
    z3 += 1
    zOld_Diff = Y
    END IF
    ELSE
    z1 += 1
    END IF
    x = y
    NEXT

    MSGBOX 0, "TBGL_Timer test results" & CRLF & _
    "Total calls: " & zLength & CRLF & _
    "No changes: " & z1 & CRLF & _
    "Total changes: " & z2 & CRLF & _
    "Diff err: " & z3

    MSGBOX 0, "Each value change: " & JOIN$(zDiff, ", ")[/code]

    You will see getTickCount reports 0.016, 0.015, 0.016, 0.016, 0.015, even on a dead loop. If you save the values.

    I threw-in the game-timer event for TBGL, just to see what it output... Not sure how to react to those numbers, but the divisor is real small, so it is all good... (Had to remove the subtracton, since that has been done by the call. Also had to remove the recording of "Not Same", since each returned value that is not 1 is unique. It returns 1 if time extends beyond 1 second or 1 frame per second.)

  8. #8
    thinBasic MVPs Michael Hartlef's Avatar
    Join Date
    Sep 2006
    Location
    Leverkusen, Germany
    Age
    51
    Posts
    3,281
    Blog Entries
    2
    Rep Power
    340

    Re: Discussion: Time base for TBEM

    Eros,

    which is the API definition behind HiResTimer_Get?

    Thanks Jason for the samples.
    Running Windows 7 Home, 64 bit, 8 GB ram, Athlon II X2 255, ATI Radeon HD 4200.

  9. #9
    thinBasic author ErosOlmi's Avatar
    Join Date
    Sep 2004
    Location
    Milan - Italy
    Age
    50
    Posts
    8,198
    Blog Entries
    2
    Rep Power
    10
    www.thinbasic.com | www.thinbasic.com/community/ | psch.thinbasic.com
    Win10Pro 64bit - 8GB Ram - Intel i7 M620 2.67GHz - NVIDIA Quadro FX1800M 1GB

  10. #10
    thinBasic MVPs Michael Hartlef's Avatar
    Join Date
    Sep 2006
    Location
    Leverkusen, Germany
    Age
    51
    Posts
    3,281
    Blog Entries
    2
    Rep Power
    340

    Re: Discussion: Time base for TBEM

    Thanks!
    Running Windows 7 Home, 64 bit, 8 GB ram, Athlon II X2 255, ATI Radeon HD 4200.

Page 1 of 2 12 LastLast

Similar Threads

  1. base conversion
    By Michael Clease in forum Sources, Templates, Code Snippets, Tips and Tricks, Do you know ...
    Replies: 3
    Last Post: 11-11-2007, 23:00

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •