Recently I worked on a game on Spritekit Engine. My question is not about spritekit, but generaly about game engines. When I write a loop code and run it (eg while i< 100000) my CPU usage goes to 100%, but when I run the test game there is no changes specially on CPU usage ,why is this so? (We know game engines runs in a loop that includes logic and graphic commands )
-
\$\begingroup\$ Usually you want to have some kind of frame limit in the loop to prevent to program from consuming 100% cpu. Like a sleep or delay function with a duration somewhere between 50 and 10 milliseconds, i.e. (1000/FPS ms). \$\endgroup\$– Emil LaineMar 23, 2015 at 6:48
1 Answer
The game engines will Sleep() or do something similar like pend on an event to not consume 100% cpu. Here is a Windows specific example that runs at a fixed frequency. On Windows XP, a Sleep(1) can take up to 2ms, so the loop takes that into account. The variables uPrev, uWait, and uRem are based on an original reading of a high speed counter, to prevent drift over time. dwLateStep is a debugging aid that is incremented every time a step has taken too long.
typedef unsigned long long UI64; /* unsigned 64 bit int */
#define FREQ 400 /* frequency */
DWORD dwLateStep; /* late step count */
LARGE_INTEGER liPerfFreq; /* 64 bit frequency */
LARGE_INTEGER liPerfTemp; /* used for query */
UI64 uFreq = FREQ; /* process frequency */
UI64 uOrig; /* original tick */
UI64 uWait; /* tick rate / freq */
UI64 uRem = 0; /* tick rate % freq */
UI64 uPrev; /* previous tick based on original tick */
UI64 uDelta; /* current tick - previous */
UI64 u2ms; /* 2ms of ticks */
UI64 i;
/* ... */ /* wait for some event to start thread */
QueryPerformanceFrequency(&liPerfFreq);
u2ms = ((UI64)(liPerfFreq.QuadPart)+499) / ((UI64)500);
timeBeginPeriod(1); /* set period to 1ms */
Sleep(128); /* wait for it to stabilize */
QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
uOrig = uPrev = liPerfTemp.QuadPart;
for(i = 0; i < (uFreq*30); i++){
/* update uWait and uRem based on uRem */
uWait = ((UI64)(liPerfFreq.QuadPart) + uRem) / uFreq;
uRem = ((UI64)(liPerfFreq.QuadPart) + uRem) % uFreq;
/* wait for uWait ticks */
while(1){
QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
uDelta = (UI64)(liPerfTemp.QuadPart - uPrev);
if(uDelta >= uWait)
break;
if((uWait - uDelta) > u2ms)
Sleep(1);
}
if(uDelta >= (uWait*2))
dwLateStep += 1;
uPrev += uWait;
/* fixed frequency code goes here */
/* along with some type of break when done */
}
timeEndPeriod(1); /* restore period */
-
\$\begingroup\$ Ok,but i think thread sleep is not accurate.for example before this, when i programming with c# i use this command as thread.sleep(10) ,it does not wait exactly for 10 ms(i test it with time stamp). \$\endgroup\$ Mar 23, 2015 at 9:50
-
\$\begingroup\$ In the case of Windows, the timeBeginPeriod(1) is supposed to set the ticker to 1000hz or 1 ms per tick, which it does on Win 7. I'm not sure about Vista. On Windows XP, the ticker is set to 1024hz, with three double delays every 125 ms (128 ticks = 125 ms), on the 42nd, 84th, and 125th "pseudo tick", so a Sleep(1) could take almost 2ms if it occurs on a double delay boundary. In the example code above, the Sleep(1) doesn't need to be accurate, it just needs to take less than 2ms. The peformance counter runs at 3+ mhz (Win 7) or at cpu clock speed (3.4ghz on my 3.4ghz cpu). \$\endgroup\$– rcgldrMar 23, 2015 at 11:10
-
\$\begingroup\$ Beware of trusting anything less than 10ms per tick. In my experience, most data structures allow you to have ridiculously small values, but that's a reflection of standard data structures, not platform capability. Also, most are fast best-effort platforms (in contrast to real-time). Any timing is not guaranteed though fast is often good enough, especially today. \$\endgroup\$– Taylor KiddMar 23, 2015 at 17:10
-
\$\begingroup\$ i also find this link helpful : gameprogrammingpatterns.com/game-loop.html \$\endgroup\$ Mar 23, 2015 at 17:49
-
\$\begingroup\$ @TaylorKidd - In the case of WIndows, timeGetDevCaps() can be used to check if timeBeginPeriod(1) actually results in a 1ms ticker. \$\endgroup\$– rcgldrMar 23, 2015 at 20:41