Skip to content

Instantly share code, notes, and snippets.

@aldelaro5
Last active June 30, 2019 17:55
Show Gist options
  • Save aldelaro5/d349f8355f1ba7bd92853afe52668a5b to your computer and use it in GitHub Desktop.
Save aldelaro5/d349f8355f1ba7bd92853afe52668a5b to your computer and use it in GitHub Desktop.
fps limiting tests
#include <algorithm>
#include <iostream>
#include <chrono>
#include <cmath>
#include <vector>
#include <Windows.h>
const int nbrSecondsToTest = 600;
const double milisecondsInSecond = 1000.0;
const double microsecondsInSecond = 1000000.0;
void printStats(std::vector<int> data, int targetFps)
{
long long sum = 0;
for (auto number : data)
sum += number;
double targetFpsInUs = microsecondsInSecond / static_cast<double>(targetFps);
double averageTimeUs = static_cast<double>(sum) / static_cast<double>(data.size());
double errorPercentage = std::abs(targetFpsInUs - averageTimeUs) / targetFpsInUs * 100;
std::cout << std::endl << "Average: " << averageTimeUs << " (" << microsecondsInSecond /
averageTimeUs << " fps)" << " which is " <<
errorPercentage << "% off" << std::endl;
std::vector<double> squaredDiff;
for (auto timeUs : data)
{
double diff = static_cast<double>(timeUs) - averageTimeUs;
squaredDiff.push_back(diff * diff);
}
double sumSquaredDiff = 0;
for (auto diff : squaredDiff)
sumSquaredDiff += diff;
double mediumDifference = sumSquaredDiff / static_cast<double>(squaredDiff.size());
std::cout << "Medium difference: " << mediumDifference << std::endl;
double standardDeviation = std::sqrt(mediumDifference);
std::cout << "Standard deviation: " << standardDeviation << " which is " << (standardDeviation /
(microsecondsInSecond / static_cast<double>(targetFps))) * 100 << "%" << std::endl;
auto min = std::min_element(data.begin(), data.end());
auto max = std::max_element(data.begin(), data.end());
std::cout << "Min time in us: " << *min << std::endl;
std::cout << "Max time in us: " << *max << std::endl << std::endl;
}
std::vector<int> waitableTimerTests(int targetFps, LARGE_INTEGER frequency)
{
int targetFpsInMS = static_cast<int>(milisecondsInSecond) / targetFps;
int nbrTimeToTest = targetFps * nbrSecondsToTest;
std::vector<int> timesVector;
HANDLE hTimer = CreateWaitableTimer(nullptr, false, nullptr);
std::cout << "Waitable timer tests..." << std::endl;
LARGE_INTEGER nextPulseTimeStamp;
QueryPerformanceCounter(&nextPulseTimeStamp);
nextPulseTimeStamp.QuadPart += (frequency.QuadPart / targetFps);
SetWaitableTimer(hTimer, &nextPulseTimeStamp, targetFpsInMS, nullptr, nullptr, false);
auto beforeTimeStamp = std::chrono::high_resolution_clock::now();
for (int i = 0; i < nbrTimeToTest; i++)
{
WaitForSingleObject(hTimer, INFINITE);
auto afterTimeStamp = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(afterTimeStamp -
beforeTimeStamp).count();
/*std::cout << "Actual time that has passed: " << duration <<
" us" << std::endl;*/
beforeTimeStamp = std::chrono::high_resolution_clock::now();
timesVector.push_back(duration);
}
return timesVector;
}
std::vector<int> sleepTests(int targetFps, LARGE_INTEGER frequency)
{
int targetFpsInMS = static_cast<int>(milisecondsInSecond) / targetFps;
long long targetFpsInTicks = frequency.QuadPart / targetFps;
int nbrTimeToTest = targetFps * nbrSecondsToTest;
std::vector<int> timesVector;
LARGE_INTEGER currentTimeStamp;
std::cout << "Sleep tests..." << std::endl;
LARGE_INTEGER nextPulseTimeStamp;
QueryPerformanceCounter(&nextPulseTimeStamp);
nextPulseTimeStamp.QuadPart += (frequency.QuadPart / targetFps);
auto beforeTimeStamp = std::chrono::high_resolution_clock::now();
for (int i = 0; i < nbrTimeToTest; i++)
{
QueryPerformanceCounter(&currentTimeStamp);
long long diffTicksUntilPulse = nextPulseTimeStamp.QuadPart - currentTimeStamp.QuadPart;
if (diffTicksUntilPulse > 0)
{
DWORD diffMsUntilPulse = (milisecondsInSecond * diffTicksUntilPulse) / frequency.QuadPart;
Sleep(diffMsUntilPulse);
}
auto afterTimeStamp = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(afterTimeStamp -
beforeTimeStamp).count();
/*std::cout << "Actual time that has passed: " << duration <<
" us" << std::endl;*/
beforeTimeStamp = std::chrono::high_resolution_clock::now();
timesVector.push_back(duration);
nextPulseTimeStamp.QuadPart = currentTimeStamp.QuadPart +
targetFpsInTicks + diffTicksUntilPulse;
}
return timesVector;
}
std::vector<int> busyWaitTests(int targetFps, LARGE_INTEGER frequency)
{
int targetFpsInMS = static_cast<int>(milisecondsInSecond) / targetFps;
long long targetFpsInTicks = frequency.QuadPart / targetFps;
int nbrTimeToTest = targetFps * nbrSecondsToTest;
std::vector<int> timesVector;
LARGE_INTEGER currentTimeStamp;
std::cout << "Busy wait tests..." << std::endl;
LARGE_INTEGER nextPulseTimeStamp;
QueryPerformanceCounter(&nextPulseTimeStamp);
nextPulseTimeStamp.QuadPart += (frequency.QuadPart / targetFps);
auto beforeTimeStamp = std::chrono::high_resolution_clock::now();
for (int i = 0; i < nbrTimeToTest; i++)
{
QueryPerformanceCounter(&currentTimeStamp);
long long diffTicksUntilPulse = nextPulseTimeStamp.QuadPart - currentTimeStamp.QuadPart;
if (diffTicksUntilPulse > 0)
{
LARGE_INTEGER timeStamp;
do
{
QueryPerformanceCounter(&timeStamp);
} while (timeStamp.QuadPart <= nextPulseTimeStamp.QuadPart);
}
auto afterTimeStamp = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(afterTimeStamp -
beforeTimeStamp).count();
/*std::cout << "Actual time that has passed: " << duration <<
" us" << std::endl;*/
beforeTimeStamp = std::chrono::high_resolution_clock::now();
timesVector.push_back(duration);
nextPulseTimeStamp.QuadPart = currentTimeStamp.QuadPart +
targetFpsInTicks + diffTicksUntilPulse;
}
return timesVector;
}
std::vector<int> sleepWithBusyWaitTests(int targetFps, LARGE_INTEGER frequency)
{
int targetFpsInMS = static_cast<int>(milisecondsInSecond) / targetFps;
long long targetFpsInTicks = frequency.QuadPart / targetFps;
int nbrTimeToTest = targetFps * nbrSecondsToTest;
std::vector<int> timesVector;
LARGE_INTEGER currentTimeStamp;
std::cout << "Sleep with busy wait tests..." << std::endl;
LARGE_INTEGER nextPulseTimeStamp;
QueryPerformanceCounter(&nextPulseTimeStamp);
nextPulseTimeStamp.QuadPart += (frequency.QuadPart / targetFps);
auto beforeTimeStamp = std::chrono::high_resolution_clock::now();
for (int i = 0; i < nbrTimeToTest; i++)
{
QueryPerformanceCounter(&currentTimeStamp);
long long diffTicksUntilPulse = nextPulseTimeStamp.QuadPart - currentTimeStamp.QuadPart;
if (diffTicksUntilPulse > 0)
{
DWORD diffMsUntilPulse = (milisecondsInSecond * diffTicksUntilPulse) / frequency.QuadPart;
Sleep(diffMsUntilPulse);
LARGE_INTEGER timeStamp;
do
{
QueryPerformanceCounter(&timeStamp);
} while (timeStamp.QuadPart <= nextPulseTimeStamp.QuadPart);
}
auto afterTimeStamp = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(afterTimeStamp -
beforeTimeStamp).count();
/*std::cout << "Actual time that has passed: " << duration <<
" us" << std::endl;*/
beforeTimeStamp = std::chrono::high_resolution_clock::now();
timesVector.push_back(duration);
nextPulseTimeStamp.QuadPart = currentTimeStamp.QuadPart +
targetFpsInTicks + diffTicksUntilPulse;
}
return timesVector;
}
int main()
{
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
int targetFps = 0;
std::cout << "Enter the target FPS: ";
std::cin >> targetFps;
std::cout << std::endl;
std::vector<int> results = waitableTimerTests(targetFps, frequency);
// The first element is always WAY off (about 2 microsecond), but isn't going to affect much
// therefore, it is removed for more representative stats
results.erase(results.begin());
printStats(results, targetFps);
results.clear();
results = sleepTests(targetFps, frequency);
printStats(results, targetFps);
results.clear();
results = busyWaitTests(targetFps, frequency);
printStats(results, targetFps);
results.clear();
results = sleepWithBusyWaitTests(targetFps, frequency);
printStats(results, targetFps);
std::cout << "Done, press enter to exit" << std::endl;
std::cin.get();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment