Last active
June 30, 2019 17:55
-
-
Save aldelaro5/d349f8355f1ba7bd92853afe52668a5b to your computer and use it in GitHub Desktop.
fps limiting tests
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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(¤tTimeStamp); | |
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(¤tTimeStamp); | |
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(¤tTimeStamp); | |
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