-
-
Notifications
You must be signed in to change notification settings - Fork 250
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
engine: platform: introduce for Platform_NanoSleep, to be used for better sleeping in between frames for lowering CPU usage #2019
base: master
Are you sure you want to change the base?
Conversation
…tter sleeping in between frames for lowering CPU usage
Not sure if Windows version of nanosleep will even work. |
Interesting stuff according to this topic: |
On my Linux server, that expectedly helped me to reduce CPU load from 100% to 20-ish% with |
Pretty good result. This is very important in case when hosting more than 1 server on the VPS/VDS. |
What should we do on Windows though, is there a reliable way to at least have a microsecond timer? That will also help decrease load on the CPU. |
CreateWaitableTimerEx with |
Right now, I'm getting CreateWaitableTimerEx with GetProcAddress and passing I think it should be fine for those who use Windows XP or 7. |
For Windows versions older that mentioned, we can fallback to |
@SNMetamorph timeBeginPeriod has millisecond accuracy, and that's useless for what I'm trying to do here. We need at least microsecond accurate sleep function or, better, nanosecond. |
I'm not sure about this. #include <windows.h>
#include <chrono>
#include <math.h>
void timerSleep(double seconds) {
using namespace std::chrono;
static HANDLE timer = CreateWaitableTimer(NULL, FALSE, NULL);
static double estimate = 5e-3;
static double mean = 5e-3;
static double m2 = 0;
static int64_t count = 1;
while (seconds - estimate > 1e-7) {
double toWait = seconds - estimate;
LARGE_INTEGER due;
due.QuadPart = -int64_t(toWait * 1e7);
auto start = high_resolution_clock::now();
SetWaitableTimerEx(timer, &due, 0, NULL, NULL, NULL, 0);
WaitForSingleObject(timer, INFINITE);
auto end = high_resolution_clock::now();
double observed = (end - start).count() / 1e9;
seconds -= observed;
++count;
double error = observed - toWait;
double delta = error - mean;
mean += delta / count;
m2 += delta * (error - mean);
double stddev = sqrt(m2 / (count - 1));
estimate = mean + stddev;
}
// spin lock
auto start = high_resolution_clock::now();
auto spinNs = int64_t(seconds * 1e9);
auto delay = nanoseconds(spinNs);
while (high_resolution_clock::now() - start < delay);
} |
Ah, I'm wrong here. This benchmark uses spin-lock for providing better accuracy, so it isn't relevant here :) P.S: but why don't we do same trick? |
Also, there is some hack with undocumented ntdll functions for Windows 7 and older, that makes possible to increase void SetTimerHighResolution()
{
#if 0
// undocumented NT API features
HMODULE hndl = GetModuleHandle("ntdll.dll");
pNtSetTimerResolution = (pfnNtSetTimerResolution)GetProcAddress(hndl, "NtSetTimerResolution");
pNtQueryTimerResolution = (pfnNtQueryTimerResolution)GetProcAddress(hndl, "NtQueryTimerResolution");
ULONG min, max, cur;
pNtQueryTimerResolution(&min, &max, &cur);
pNtSetTimerResolution(max, 1, &cur);
#else
TIMECAPS tc;
timeGetDevCaps(&tc, sizeof(TIMECAPS));
timeBeginPeriod(tc.wPeriodMin);
#endif
} P.S: For some reason, |
There were reports about high CPU usage with high framerates, especially on the servers.
As it seems, it might've been caused by insufficient accuracy of
Platform_Sleep
, which only accepts milliseconds. If we imagine game running at 500+ FPS,Platform_Sleep
instantly becomes useless.With this PR,
sleeptime
cvar resolution changes from milliseconds down to microseconds.