Thread Tools Display Modes
09-28-20, 09:50 AM   #1
KL1SK
A Murloc Raider
Join Date: Sep 2020
Posts: 9
Question GetTime()

Is there a more precise function GetTime ()?
When I call GetTime () every millisecond, I don't get the exact time, about 5-10 milliseconds are wasted.
Rather, GetTime () returns the same value several times, and then immediately returns + 5-10 milliseconds.
.888
.888
.888
.891
.892
.893
.893
.893
etc
  Reply With Quote
09-28-20, 12:27 PM   #2
wardz
A Deviate Faerie Dragon
 
wardz's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 17
Code:
GetTime() - GetTickTime()
or
Code:
debugprofilestop()
I guess.
Why do you need such precision?
  Reply With Quote
09-28-20, 01:58 PM   #3
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,308
To answer the question directly, GetTime() is cached at the start of every frame.

PS: Be careful if you use debugprofilestop(). The number resets if anyone runs debugprofilestart().
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
09-29-20, 01:17 AM   #4
KL1SK
A Murloc Raider
Join Date: Sep 2020
Posts: 9
I want to organize the work of a loop with a dynamic load on the frame. To avoid stuttering when working with the "for" operator. For this, it is desirable to have accurate data.
  Reply With Quote
09-29-20, 11:07 AM   #5
jeruku
A Cobalt Mageweaver
 
jeruku's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2010
Posts: 223
Originally Posted by KL1SK View Post
I want to organize the work of a loop with a dynamic load on the frame. To avoid stuttering when working with the "for" operator. For this, it is desirable to have accurate data.
You mean something along the lines of a queue?
__________________
"I have not failed, I simply found 10,000 ways that did not work." - Thomas Edison
  Reply With Quote
09-29-20, 12:23 PM   #6
KL1SK
A Murloc Raider
Join Date: Sep 2020
Posts: 9
Originally Posted by jeruku View Post
You mean something along the lines of a queue?
Depending on the delta of the frame time, the cycle time in the frame is regulated. Yes it can be called a queue.
  Reply With Quote
09-29-20, 04:59 PM   #7
Ketho
A Pyroguard Emberseer
 
Ketho's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,026
If you want help with your code, SHOW YOUR CODE!
  Reply With Quote
09-30-20, 02:40 PM   #8
KL1SK
A Murloc Raider
Join Date: Sep 2020
Posts: 9
There is such code:
Lua Code:
  1. local EM = CreateFrame('Frame')
  2.  
  3. testTable = {}
  4. for i = 1, 100000 do
  5.     testTable[i] = i
  6. end
  7.  
  8. function DefPairs()
  9.     local startTime = GetTime()
  10.     for k, v in pairs(testTable) do
  11.         print(k)
  12.     end
  13.     print("LeadTime: " .. GetTime() - startTime)
  14. end
  15.  
  16. function SmoothPairs(table, mult, callback)
  17.     EM:SetScript("OnUpdate", nil)
  18.  
  19.     local prevFrameTime     = 0
  20.     local startTime         = GetTime()
  21.     local index, var        = nil, nil
  22.  
  23.     EM:SetScript("OnUpdate", function(self, elapsed)
  24.         if prevFrameTime == 0 then
  25.             prevFrameTime = GetTime()
  26.         else
  27.             local startFrameTime    = GetTime()
  28.             local frameDeltaTime    = startFrameTime - prevFrameTime
  29.             prevFrameTime           = startFrameTime
  30.  
  31.             local timeForWork       = ( 0.0001 / frameDeltaTime ) * mult
  32.             local stopTaskTime      = startFrameTime + timeForWork
  33.  
  34.             while GetTime() < stopTaskTime do
  35.                 index, var = next(table, index)
  36.                 if not index then
  37.                     EM:SetScript("OnUpdate", nil)
  38.                     print("LeadTime: " .. GetTime() - startTime)
  39.                     return
  40.                 else
  41.                     callback(index, var)
  42.                 end
  43.             end
  44.         end
  45.     end)
  46. end
  47.  
  48. function StopSmoothPairs()
  49.     EM:SetScript("OnUpdate", nil)
  50. end
  51.  
  52. -- /run SmoothPairs(testTable, 1, function(k, v) print(k) end)
  53. -- /run StopSmoothPairs()
  54. -- /run DefPairs()

But it does not work as expected. Assault GetTime() returns always the same value inside the frame.
Later try to check using "anim:SetScript("OnLoop", ...)", he seems to have no such restrictions.

Last edited by KL1SK : 09-30-20 at 04:02 PM.
  Reply With Quote
10-01-20, 12:56 AM   #9
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,308
This is what I've come up with.
Lua Code:
  1. local AsyncPairs; do
  2. --  Local pointers make lookups faster
  3.     local CallErrorHandler=CallErrorHandler;
  4.     local debugprofilestop=debugprofilestop;
  5.     local next=next;
  6.     local table_remove=table.remove;
  7.     local unpack=unpack;
  8.     local xpcall=xpcall;
  9.  
  10.     local TableStack={};
  11.     local TimerFrame=CreateFrame("Frame");
  12.  
  13.     local function TimerFrame_OnUpdate(self,elpased)
  14.         local tbldat=TableStack[1];--   Pull from bottom of stack
  15.         if tbldat then
  16.             local tbl,key,limit,oniterate,onfinish=unpack(tbldat,1,5);--    Extract vars; range is specified since there can be embedded nils
  17.             local start=debugprofilestop()-elapsed*1000;--  Offset with frame render time
  18.             repeat--    Used instead of While to guarantee at least one iteration
  19.                 local val; key,val=next(tbl,key);-- Get next key/val pair
  20.                 if key~=nil then--  We have a key/val pair
  21.                     local ok=xpcall(oniterate,CallErrorHandler,key,val);--  Dispatch OnIterate callback
  22.                     if not ok then table_remove(TableStack,1); break; end-- OnError: Remove table from stack and exit
  23.                 else--  No more key/val pairs
  24.                     table_remove(TableStack,1);--   Remove from stack
  25.                     if onfinish then xpcall(onfinish,CallErrorHandler); end--   Dispatch OnFinish callback
  26.                     break;--    Exit loop
  27.                 end
  28.             until debugprofilestop()-start>=limit-- Checked at end of iteration; compare elapsed time to limit
  29.             tbldat[2]=key;--    Store new key for next frame; if table was removed from stack, this is just garbage anyway
  30.         else self:SetScript("OnUpdate",nil); end--  Disable if we have nothing on the stack
  31.     end
  32.  
  33.     function AsyncPairs(tbl,limit,oniterate,onfinish)
  34.         table.insert(TableStack,{tbl,nil,limit,oniterate,onfinish});--  Push to stack
  35.         TimerFrame:SetScript("OnUpdate",TimerFrame_OnUpdate);-- Activate timer function
  36.     end
  37. end

Usage: AsyncPairs(Table, Limit, OnIterate[, OnFinish])
Limit is in milliseconds. OnIterate and OnFinish are callbacks with the later being optional.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 10-01-20 at 12:59 AM.
  Reply With Quote
10-01-20, 02:00 AM   #10
KL1SK
A Murloc Raider
Join Date: Sep 2020
Posts: 9
If I understood correctly "Debug profiling" works constantly. debugprofilestop () just returns its running time?
  Reply With Quote
10-01-20, 11:56 PM   #11
KL1SK
A Murloc Raider
Join Date: Sep 2020
Posts: 9
@SDPhantom thanks for xpcall, never used such a useful feature before . And also does not eat up performance.
I decided to abandon OnUpdate in favor of C_Timer, it turned out to be a little cheaper. In addition to this, you can run multiple loops in parallel and they work great. Possibly the engine runs them in different threads, since the load does not grow proportionally. And thanks to the inversely proportional dependence on the delta-time of the frame, work on a system of different performance is simply excellent.
Probably we still need to work on smoothing the start. With very high productivity, you can get a noticeable drawdown, which is different from the working one.

Last edited by KL1SK : 10-02-20 at 12:00 AM.
  Reply With Quote
10-02-20, 05:57 AM   #12
kurapica.igas
A Chromatic Dragonspawn
Join Date: Aug 2011
Posts: 152
If you don't mind use a lib, you may check the example for Continue() in the Scorpio - asynchronous framework, the async frame work is designed to smooth the operation of codes and make the fps stable.

The async framework is also designed based on the debugprofilestop. The lib can be used as an API lib, works like :

Lua Code:
  1. Scorpio.Continue(function()
  2.     -- The function is processed within in a coroutine
  3.     -- so, async API can be used
  4.     local testTable = {}
  5.     for i = 1, 100000 do
  6.         testTable[i] = i
  7.     end
  8.  
  9.     local startTime = GetTime()
  10.  
  11.     for k, v in pairs(testTable) do
  12.         -- The Continue API will yield the current coroutine
  13.         -- And then check if there is still enough time to resume it
  14.         -- So the fps can be protected
  15.         Scorpio.Continue()
  16.  
  17.         -- If the process is yield, it'll be resumed in the next phase(with OnUpdate)
  18.         -- So we can check how many the recycle processed in one phase
  19.         if GetTime() ~= startTime then
  20.             print(k)
  21.             startTime = GetTime()
  22.         end
  23.     end
  24. end)

I prefer the Lua's coroutine compares to the callback since the logic can be put together
  Reply With Quote
10-02-20, 06:49 AM   #13
KL1SK
A Murloc Raider
Join Date: Sep 2020
Posts: 9
Originally Posted by kurapica.igas View Post
I prefer the Lua's coroutine compares to the callback since the logic can be put together
Yes, I was considering using 'coroutine'. But does it have any advantages other than convenience?
Based on my practice, beautiful code in most cases ~= fast code.
  Reply With Quote
10-02-20, 08:10 PM   #14
kurapica.igas
A Chromatic Dragonspawn
Join Date: Aug 2011
Posts: 152
Originally Posted by KL1SK View Post
Yes, I was considering using 'coroutine'. But does it have any advantages other than convenience?
Based on my practice, beautiful code in most cases ~= fast code.
The coroutine are common Lua functions, the main cost of it compares to the normal Lua func is about two parts:

I. The cost of the coroutine's creation is much bigger than call a function, so I build a thread pool to re-use them, you may check the PLoop/Threading.lua#L53-L135, that's an example how to build a thread pool in Lua(Normally people do this in C part, the logic is a little complex). I use __Async__ to wrap the functions, so they can be processed in re-used coroutines.

II. The cost of the coroutine context's switch, the cost is tiny but I can't say that can be ignored, it's a price for putting the logic together.

For async codes, they are processed in several phases, that's why I build a framework for them, I use a task schedule manager to control all those async tasks, so the process can be seperated into several phases without dropping the fps.

So I don't try to reduce the corotuine's switch cost since I can do nothing about it, I just manage the time slice of async tasks to make the game smooth. And that's what can't be done with normal Lua functions, since we can't yield/resume them.

For daily works, the main reason of using coroutines is to avoid the callbacks, but for now the callback mechanism is more popular in the WOW, since most logic is not that complex compares to daily works. And it's a really hard job to make an useful coroutine framework.

For me, the beauty and readability is important, and some useful mechanism can be bring in like get user input directly Scorpio the-user-interaction-apis and much more.

Last edited by kurapica.igas : 10-02-20 at 08:28 PM.
  Reply With Quote
10-03-20, 03:01 AM   #15
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,308
Originally Posted by KL1SK View Post
If I understood correctly "Debug profiling" works constantly. debugprofilestop () just returns its running time?
debugprofilestop() returns the number of milliseconds since the last time debugprofilestart() was called. The value is undefined if the later has never been called, but seems to tick at the same rate anyway.

The idea of these two functions is a misnomer. There isn't some "profiling" code behind them. One stores the current time in memory and the other compares that with the current time.



Originally Posted by KL1SK View Post
thanks for xpcall, never used such a useful feature before . And also does not eat up performance.
xpcall() and pcall() makes the function given run in protected mode, which means any errors encountered returns to the caller. It's useful in code like event handlers and callback libraries that need to run a list of registered functions, any of which will cause cascading problems if an unprotected error occurs. xpcall() lets you specify a custom error handler to run, however I just pipe in the existing one so it still gets the proper call stack in the debug data.



Originally Posted by KL1SK View Post
I decided to abandon OnUpdate in favor of C_Timer, it turned out to be a little cheaper. In addition to this, you can run multiple loops in parallel and they work great. Possibly the engine runs them in different threads, since the load does not grow proportionally.
Lua is a single-threaded environment, meaning it doesn't support true asynchronous threads. OnUpdate guarantees that a frame goes out in between each time the function runs. C_Timer on the other hand adds the function to an internal scheduler that tries to run the function as close to the given time as it can when the engine isn't busy. It is capable of running multiple times in between two frames, so it isn't as reliable in limiting how often a function is run, especially when given very short delays.

A general rule of thumb I follow. OnUpdate when you have a long task that you need to spread out over time. C_Timer when you have a small task that needs to be repeated, but not as frequent.



Originally Posted by KL1SK View Post
I was considering using 'coroutine'. But does it have any advantages other than convenience?
It's just personal preference. Coroutines can be very powerful if you know how to use them. Realistically, I've never really needed them.

What a coroutine does is lets you pause execution of its assigned function and resume it later. Its more specialized use is for multi-stage functions that have a list of multiple heavy purpose tasks you want to spread over time.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 10-03-20 at 03:06 AM.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » GetTime()

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off