Common: Purge CPU frequency measurement

It's not accurate, and we can query the registry for the TSC frequency
for thread timers.

Also replaces InitCPUTicks() with a global constructor.
This commit is contained in:
Stenzek 2023-06-15 01:25:48 +10:00 committed by Connor McLaughlin
parent 5d64a2b889
commit 8a8e6c5d20
9 changed files with 26 additions and 97 deletions

View File

@ -1,5 +1,5 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2014 PCSX2 Dev Team
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
@ -52,15 +52,12 @@ u64 GetPhysicalMemory()
return getmem;
}
static u64 tickfreq;
static mach_timebase_info_data_t s_timebase_info;
void InitCPUTicks()
{
static const u64 tickfreq = []() {
if (mach_timebase_info(&s_timebase_info) != KERN_SUCCESS)
abort();
tickfreq = (u64)1e9 * (u64)s_timebase_info.denom / (u64)s_timebase_info.numer;
}
return (u64)1e9 * (u64)s_timebase_info.denom / (u64)s_timebase_info.numer;
}();
// returns the performance-counter frequency: ticks per second (Hz)
//

View File

@ -196,7 +196,6 @@ private:
#define SafeSysMunmap(ptr, size) \
((void)(HostSys::Munmap(ptr, size), (ptr) = 0))
extern void InitCPUTicks();
extern u64 GetTickFrequency();
extern u64 GetCPUTicks();
extern u64 GetPhysicalMemory();

View File

@ -47,11 +47,6 @@ u64 GetPhysicalMemory()
return pages * getpagesize();
}
void InitCPUTicks()
{
}
u64 GetTickFrequency()
{
return 1000000000; // unix measures in nanoseconds

View File

@ -29,7 +29,12 @@
#include <timeapi.h>
#include <VersionHelpers.h>
alignas(16) static LARGE_INTEGER lfreq;
// If anything tries to read this as an initializer, we're in trouble.
static const LARGE_INTEGER lfreq = []() {
LARGE_INTEGER ret = {};
QueryPerformanceFrequency(&ret);
return ret;
}();
// This gets leaked... oh well.
static thread_local HANDLE s_sleep_timer;
@ -48,11 +53,6 @@ static HANDLE GetSleepTimer()
return s_sleep_timer;
}
void InitCPUTicks()
{
QueryPerformanceFrequency(&lfreq);
}
u64 GetTickFrequency()
{
return lfreq.QuadPart;

View File

@ -217,7 +217,20 @@ u64 Threading::GetThreadTicksPerSecond()
// So, the frequency is our base clock speed (and stable regardless of power management).
static u64 frequency = 0;
if (unlikely(frequency == 0))
frequency = x86caps.CachedMHz() * u64(1000000);
{
HKEY key;
LSTATUS res =
RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &key);
if (res == ERROR_SUCCESS)
{
DWORD mhz;
DWORD size = sizeof(mhz);
res = RegQueryValueExW(key, L"~MHz", nullptr, nullptr, reinterpret_cast<LPBYTE>(&mhz), &size);
if (res == ERROR_SUCCESS)
frequency = static_cast<u64>(mhz) * static_cast<u64>(1000000);
RegCloseKey(key);
}
}
return frequency;
}

View File

@ -106,49 +106,6 @@ void x86capabilities::SIMD_EstablishMXCSRmask()
MXCSR_Mask.bitmask = result;
}
// Counts the number of cpu cycles executed over the requested number of PerformanceCounter
// ticks. Returns that exact count.
// For best results you should pick a period of time long enough to get a reading that won't
// be prone to rounding error; but short enough that it'll be highly unlikely to be interrupted
// by the operating system task switches.
s64 x86capabilities::_CPUSpeedHz(u64 time) const
{
u64 timeStart, timeStop;
s64 startCycle, endCycle;
if (!hasTimeStampCounter)
return 0;
SingleCoreAffinity affinity_lock;
// Align the cpu execution to a cpuTick boundary.
do
{
timeStart = GetCPUTicks();
startCycle = __rdtsc();
} while (GetCPUTicks() == timeStart);
do
{
timeStop = GetCPUTicks();
endCycle = __rdtsc();
} while ((timeStop - timeStart) < time);
s64 cycleCount = endCycle - startCycle;
s64 timeCount = timeStop - timeStart;
s64 overrun = timeCount - time;
if (!overrun)
return cycleCount;
// interference could cause us to overshoot the target time, compensate:
double cyclesPerTick = (double)cycleCount / (double)timeCount;
double newCycleCount = (double)cycleCount - (cyclesPerTick * overrun);
return (s64)newCycleCount;
}
const char* x86capabilities::GetTypeName() const
{
switch (TypeID)
@ -307,28 +264,3 @@ void x86capabilities::Identify()
isIdentified = true;
}
u32 x86capabilities::CalculateMHz() const
{
InitCPUTicks();
u64 span = GetTickFrequency();
if ((span % 1000) < 400) // helps minimize rounding errors
return (u32)(_CPUSpeedHz(span / 1000) / 1000);
else
return (u32)(_CPUSpeedHz(span / 500) / 2000);
}
u32 x86capabilities::CachedMHz()
{
static std::atomic<u32> cached{0};
u32 local = cached.load(std::memory_order_relaxed);
if (unlikely(local == 0))
{
x86capabilities caps;
caps.Identify();
local = caps.CalculateMHz();
cached.store(local, std::memory_order_relaxed);
}
return local;
}

View File

@ -116,13 +116,9 @@ public:
void CountCores();
const char* GetTypeName() const;
static u32 CachedMHz();
u32 CalculateMHz() const;
void SIMD_EstablishMXCSRmask();
protected:
s64 _CPUSpeedHz(u64 time) const;
void CountLogicalCores();
};

View File

@ -103,19 +103,17 @@ void SysLogMachineCaps()
GetOSVersionString().c_str(),
(u32)(GetPhysicalMemory() / _1mb));
u32 speed = x86caps.CalculateMHz();
Console.Indent().WriteLn(
"CPU name = %s\n"
"Vendor/Model = %s (stepping %02X)\n"
"CPU speed = %u.%03u ghz (%u logical thread%ls)\n"
"Logical Cores = %u\n"
"x86PType = %s\n"
"x86Flags = %08x %08x\n"
"x86EFlags = %08x",
x86caps.FamilyName,
x86caps.VendorName, x86caps.StepID,
speed / 1000, speed % 1000,
x86caps.LogicalCores, (x86caps.LogicalCores == 1) ? L"" : L"s",
x86caps.LogicalCores,
x86caps.GetTypeName(),
x86caps.Flags, x86caps.Flags2,
x86caps.EFlags);

View File

@ -306,7 +306,6 @@ bool VMManager::Internal::CPUThreadInitialize()
x86caps.Identify();
x86caps.CountCores();
x86caps.SIMD_EstablishMXCSRmask();
x86caps.CalculateMHz();
SysLogMachineCaps();
pxAssert(!s_vm_memory && !s_cpu_provider_pack);