Add new TimeSpan class for more accurate timing.

Minimizes the amount of value conversions and performs subtractions in
integer space.
This commit is contained in:
Henrik Rydgård 2024-06-05 10:29:04 +02:00
parent 3e26866f76
commit fa5ec667ef
3 changed files with 88 additions and 8 deletions

View file

@ -4,6 +4,7 @@
#include <cstdint>
#include "Common/TimeUtil.h"
#include "Common/Log.h"
#ifdef HAVE_LIBNX
#include <switch.h>
@ -31,8 +32,8 @@
// TODO: https://github.com/floooh/sokol/blob/9a6237fcdf213e6da48e4f9201f144bcb2dcb46f/sokol_time.h#L229-L248
static const double micros = 1000000.0;
static const double nanos = 1000000000.0;
constexpr double micros = 1000000.0;
constexpr double nanos = 1000000000.0;
#if PPSSPP_PLATFORM(WINDOWS)
@ -40,12 +41,16 @@ static LARGE_INTEGER frequency;
static double frequencyMult;
static LARGE_INTEGER startTime;
double time_now_d() {
static inline void InitTime() {
if (frequency.QuadPart == 0) {
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&startTime);
frequencyMult = 1.0 / static_cast<double>(frequency.QuadPart);
}
}
double time_now_d() {
InitTime();
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
double elapsed = static_cast<double>(time.QuadPart - startTime.QuadPart);
@ -86,6 +91,22 @@ void yield() {
YieldProcessor();
}
TimeSpan::TimeSpan() {
_dbg_assert_(frequencyMult != 0.0);
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *>(&nativeStart_));
}
double TimeSpan::ElapsedSeconds() const {
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
double elapsed = static_cast<double>(time.QuadPart - nativeStart_);
return elapsed * frequencyMult;
}
int64_t TimeSpan::ElapsedNanos() const {
return (int64_t)(ElapsedSeconds() * 1000000000.0);
}
#elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(LINUX) || PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)
// The only intended use is to match the timings in VK_GOOGLE_display_timing
@ -128,10 +149,34 @@ void yield() {
#endif
}
TimeSpan::TimeSpan() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
nativeStart_ = ts.tv_sec;
nsecs_ = ts.tv_nsec;
}
int64_t TimeSpan::ElapsedNanos() const {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
int64_t secs = ts.tv_sec - nativeStart_;
int64_t nsecs = ts.tv_nsec - nsecs_;
if (nsecs < 0) {
secs--;
nsecs += 1000000000;
}
return secs * 1000000000ULL + nsecs;
}
double TimeSpan::ElapsedSeconds() const {
return (double)ElapsedNanos() * (1.0 / nanos);
}
#else
static time_t start;
double time_now_d() {
static time_t start;
struct timeval tv;
gettimeofday(&tv, nullptr);
if (start == 0) {
@ -141,7 +186,6 @@ double time_now_d() {
}
uint64_t time_now_raw() {
static time_t start;
struct timeval tv;
gettimeofday(&tv, nullptr);
if (start == 0) {
@ -164,6 +208,30 @@ double time_now_unix_utc() {
return time_now_raw();
}
TimeSpan::TimeSpan() {
struct timeval tv;
gettimeofday(&tv, nullptr);
nativeStart_ = tv.tv_sec;
nsecs_ = tv.tv_usec;
}
int64_t TimeSpan::ElapsedNanos() const {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
int64_t secs = ts.tv_sec - nativeStart_;
int64_t usecs = ts.tv_nsec - nsecs_;
if (usecs < 0) {
secs--;
usecs += 1000000;
}
return secs * 1000000000 + usecs * 1000;
}
double TimeSpan::ElapsedSeconds() const {
return (double)ElapsedNanos() * (1.0 / 1000000000);
}
#endif
void sleep_ms(int ms) {

View file

@ -1,5 +1,4 @@
#pragma once
#include <cstdint>
// Seconds.
@ -30,10 +29,23 @@ public:
static Instant Now() {
return Instant(time_now_d());
}
double Elapsed() const {
double ElapsedSeconds() const {
return time_now_d() - instantTime_;
}
private:
explicit Instant(double initTime) : instantTime_(initTime) {}
double instantTime_;
};
// Most accurate timer possible - no extra double conversions. Only for spans.
class TimeSpan {
public:
TimeSpan();
double ElapsedSeconds() const;
int64_t ElapsedNanos() const;
private:
uint64_t nativeStart_;
#ifndef _WIN32
int64_t nsecs_;
#endif
};

View file

@ -119,7 +119,7 @@ bool TestMultithreadedScheduling() {
threads.clear();
printf("Stress test elapsed: %0.2f", start.Elapsed());
printf("Stress test elapsed: %0.2f", start.ElapsedSeconds());
return true;
}