mirror of https://github.com/PCSX2/pcsx2.git
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:
parent
5d64a2b889
commit
8a8e6c5d20
|
@ -1,5 +1,5 @@
|
||||||
/* PCSX2 - PS2 Emulator for PCs
|
/* 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
|
* 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-
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||||
|
@ -52,15 +52,12 @@ u64 GetPhysicalMemory()
|
||||||
return getmem;
|
return getmem;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u64 tickfreq;
|
|
||||||
static mach_timebase_info_data_t s_timebase_info;
|
static mach_timebase_info_data_t s_timebase_info;
|
||||||
|
static const u64 tickfreq = []() {
|
||||||
void InitCPUTicks()
|
|
||||||
{
|
|
||||||
if (mach_timebase_info(&s_timebase_info) != KERN_SUCCESS)
|
if (mach_timebase_info(&s_timebase_info) != KERN_SUCCESS)
|
||||||
abort();
|
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)
|
// returns the performance-counter frequency: ticks per second (Hz)
|
||||||
//
|
//
|
||||||
|
|
|
@ -196,7 +196,6 @@ private:
|
||||||
#define SafeSysMunmap(ptr, size) \
|
#define SafeSysMunmap(ptr, size) \
|
||||||
((void)(HostSys::Munmap(ptr, size), (ptr) = 0))
|
((void)(HostSys::Munmap(ptr, size), (ptr) = 0))
|
||||||
|
|
||||||
extern void InitCPUTicks();
|
|
||||||
extern u64 GetTickFrequency();
|
extern u64 GetTickFrequency();
|
||||||
extern u64 GetCPUTicks();
|
extern u64 GetCPUTicks();
|
||||||
extern u64 GetPhysicalMemory();
|
extern u64 GetPhysicalMemory();
|
||||||
|
|
|
@ -47,11 +47,6 @@ u64 GetPhysicalMemory()
|
||||||
return pages * getpagesize();
|
return pages * getpagesize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void InitCPUTicks()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 GetTickFrequency()
|
u64 GetTickFrequency()
|
||||||
{
|
{
|
||||||
return 1000000000; // unix measures in nanoseconds
|
return 1000000000; // unix measures in nanoseconds
|
||||||
|
|
|
@ -29,7 +29,12 @@
|
||||||
#include <timeapi.h>
|
#include <timeapi.h>
|
||||||
#include <VersionHelpers.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.
|
// This gets leaked... oh well.
|
||||||
static thread_local HANDLE s_sleep_timer;
|
static thread_local HANDLE s_sleep_timer;
|
||||||
|
@ -48,11 +53,6 @@ static HANDLE GetSleepTimer()
|
||||||
return s_sleep_timer;
|
return s_sleep_timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitCPUTicks()
|
|
||||||
{
|
|
||||||
QueryPerformanceFrequency(&lfreq);
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 GetTickFrequency()
|
u64 GetTickFrequency()
|
||||||
{
|
{
|
||||||
return lfreq.QuadPart;
|
return lfreq.QuadPart;
|
||||||
|
|
|
@ -217,7 +217,20 @@ u64 Threading::GetThreadTicksPerSecond()
|
||||||
// So, the frequency is our base clock speed (and stable regardless of power management).
|
// So, the frequency is our base clock speed (and stable regardless of power management).
|
||||||
static u64 frequency = 0;
|
static u64 frequency = 0;
|
||||||
if (unlikely(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;
|
return frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,49 +106,6 @@ void x86capabilities::SIMD_EstablishMXCSRmask()
|
||||||
MXCSR_Mask.bitmask = result;
|
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
|
const char* x86capabilities::GetTypeName() const
|
||||||
{
|
{
|
||||||
switch (TypeID)
|
switch (TypeID)
|
||||||
|
@ -307,28 +264,3 @@ void x86capabilities::Identify()
|
||||||
|
|
||||||
isIdentified = true;
|
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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -116,13 +116,9 @@ public:
|
||||||
void CountCores();
|
void CountCores();
|
||||||
const char* GetTypeName() const;
|
const char* GetTypeName() const;
|
||||||
|
|
||||||
static u32 CachedMHz();
|
|
||||||
u32 CalculateMHz() const;
|
|
||||||
|
|
||||||
void SIMD_EstablishMXCSRmask();
|
void SIMD_EstablishMXCSRmask();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
s64 _CPUSpeedHz(u64 time) const;
|
|
||||||
void CountLogicalCores();
|
void CountLogicalCores();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -103,19 +103,17 @@ void SysLogMachineCaps()
|
||||||
GetOSVersionString().c_str(),
|
GetOSVersionString().c_str(),
|
||||||
(u32)(GetPhysicalMemory() / _1mb));
|
(u32)(GetPhysicalMemory() / _1mb));
|
||||||
|
|
||||||
u32 speed = x86caps.CalculateMHz();
|
|
||||||
|
|
||||||
Console.Indent().WriteLn(
|
Console.Indent().WriteLn(
|
||||||
"CPU name = %s\n"
|
"CPU name = %s\n"
|
||||||
"Vendor/Model = %s (stepping %02X)\n"
|
"Vendor/Model = %s (stepping %02X)\n"
|
||||||
"CPU speed = %u.%03u ghz (%u logical thread%ls)\n"
|
"Logical Cores = %u\n"
|
||||||
"x86PType = %s\n"
|
"x86PType = %s\n"
|
||||||
"x86Flags = %08x %08x\n"
|
"x86Flags = %08x %08x\n"
|
||||||
"x86EFlags = %08x",
|
"x86EFlags = %08x",
|
||||||
x86caps.FamilyName,
|
x86caps.FamilyName,
|
||||||
x86caps.VendorName, x86caps.StepID,
|
x86caps.VendorName, x86caps.StepID,
|
||||||
speed / 1000, speed % 1000,
|
x86caps.LogicalCores,
|
||||||
x86caps.LogicalCores, (x86caps.LogicalCores == 1) ? L"" : L"s",
|
|
||||||
x86caps.GetTypeName(),
|
x86caps.GetTypeName(),
|
||||||
x86caps.Flags, x86caps.Flags2,
|
x86caps.Flags, x86caps.Flags2,
|
||||||
x86caps.EFlags);
|
x86caps.EFlags);
|
||||||
|
|
|
@ -306,7 +306,6 @@ bool VMManager::Internal::CPUThreadInitialize()
|
||||||
x86caps.Identify();
|
x86caps.Identify();
|
||||||
x86caps.CountCores();
|
x86caps.CountCores();
|
||||||
x86caps.SIMD_EstablishMXCSRmask();
|
x86caps.SIMD_EstablishMXCSRmask();
|
||||||
x86caps.CalculateMHz();
|
|
||||||
SysLogMachineCaps();
|
SysLogMachineCaps();
|
||||||
|
|
||||||
pxAssert(!s_vm_memory && !s_cpu_provider_pack);
|
pxAssert(!s_vm_memory && !s_cpu_provider_pack);
|
||||||
|
|
Loading…
Reference in New Issue