forked from mirrors/0ad
robustify CPU freq detection and timer code, hopefully solve lockup issue
cpu: cache CPU info to prevent calling freq measurement code several times config: mention e.g. fopen_s instead of only the secure string functions ia32: exception-safe scheduler setting; no longer use absolute max priority (risky, could hang machine if loop contains a bug) wtime: add note on TSC safety, slight improvements timer: try and prevent timer from returning the same value This was SVN commit r5075.
This commit is contained in:
+1
-1
@@ -396,7 +396,7 @@
|
||||
# define HAVE_STL_HASH 0
|
||||
#endif
|
||||
|
||||
// safe string functions: strcpy_s et al.
|
||||
// safe CRT functions: strcpy_s, fopen_s, etc.
|
||||
// these are always available to users: if not provided by the CRT, we
|
||||
// implement them ourselves. this flag is only used to skip our impl.
|
||||
#if MSC_VERSION >= 1400
|
||||
|
||||
+108
-85
@@ -34,108 +34,56 @@ AT_STARTUP(\
|
||||
)
|
||||
|
||||
|
||||
static ModuleInitState module_init_state = MODULE_BEFORE_INIT;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
#pragma region Accessor functions
|
||||
// prevent other modules from changing the underlying data.
|
||||
// insulate caller from the system-specific modules and cache results.
|
||||
// note: the providers sometimes need to store the results anyway, so we
|
||||
// don't need to do caching in those cases.
|
||||
// these are set once during cpu_Init since they're usually all used and
|
||||
// we thus avoid needing if(already_called) return old_result.
|
||||
// initially set to 'impossible' values to catch uses before cpu_Init.
|
||||
|
||||
bool cpu_IsModuleInitialized()
|
||||
{
|
||||
return module_init_state == MODULE_INITIALIZED;
|
||||
}
|
||||
static ModuleInitState module_init_state = MODULE_BEFORE_INIT;
|
||||
static double clock_frequency = -1.0;
|
||||
static bool is_throttling_possible = true;
|
||||
static size_t page_size = 1;
|
||||
static size_t memory_total_mib = 1;
|
||||
|
||||
const char* cpu_IdentifierString()
|
||||
|
||||
static void DetectClockFrequency()
|
||||
{
|
||||
#if CPU_IA32
|
||||
return ia32_IdentifierString();
|
||||
#endif
|
||||
}
|
||||
|
||||
double cpu_ClockFrequency()
|
||||
{
|
||||
#if CPU_IA32
|
||||
return ia32_ClockFrequency(); // authoritative, precise
|
||||
#endif
|
||||
}
|
||||
|
||||
uint cpu_NumPackages()
|
||||
{
|
||||
#if CPU_IA32
|
||||
return ia32_NumPackages();
|
||||
#endif
|
||||
}
|
||||
|
||||
uint cpu_CoresPerPackage()
|
||||
{
|
||||
#if CPU_IA32
|
||||
return ia32_CoresPerPackage();
|
||||
#endif
|
||||
}
|
||||
|
||||
uint cpu_LogicalPerCore()
|
||||
{
|
||||
#if CPU_IA32
|
||||
return ia32_LogicalPerCore();
|
||||
clock_frequency = ia32_ClockFrequency(); // authoritative, precise
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool cpu_IsThrottlingPossible()
|
||||
static void DetectIfThrottlingPossible()
|
||||
{
|
||||
#if CPU_IA32
|
||||
if(ia32_IsThrottlingPossible() == 1)
|
||||
return true;
|
||||
{
|
||||
is_throttling_possible = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OS_WIN
|
||||
if(wcpu_IsThrottlingPossible() == 1)
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
//-----------------------------------------------------------------------------
|
||||
// memory
|
||||
|
||||
static size_t cpu_page_size = 0;
|
||||
// determined during cpu_Init; cleaned up and given in MiB
|
||||
static size_t cpu_memory_total_mib = 0;
|
||||
|
||||
// System V derived (GNU/Linux, Solaris)
|
||||
#if defined(_SC_AVPHYS_PAGES)
|
||||
|
||||
static int SysconfFromMemType(CpuMemoryIndicators mem_type)
|
||||
{
|
||||
switch(mem_type)
|
||||
{
|
||||
case CPU_MEM_TOTAL:
|
||||
return _SC_PHYS_PAGES;
|
||||
case CPU_MEM_AVAILABLE:
|
||||
return _SC_AVPHYS_PAGES;
|
||||
is_throttling_possible = true;
|
||||
return;
|
||||
}
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
size_t cpu_MemorySize(CpuMemoryIndicators mem_type)
|
||||
{
|
||||
// quasi-POSIX
|
||||
#if defined(_SC_AVPHYS_PAGES)
|
||||
const int sc_name = SysconfFromMemType(mem_type);
|
||||
const size_t memory_size = sysconf(sc_name) * cpu_page_size;
|
||||
return memory_size;
|
||||
// BSD / Mac OS X
|
||||
#else
|
||||
return bsd_MemorySize(mem_type);
|
||||
#endif
|
||||
is_throttling_possible = false;
|
||||
}
|
||||
|
||||
|
||||
static size_t DetermineMemoryTotalMiB()
|
||||
static void DetectMemory()
|
||||
{
|
||||
page_size = (size_t)sysconf(_SC_PAGESIZE);
|
||||
|
||||
size_t memory_total = cpu_MemorySize(CPU_MEM_TOTAL);
|
||||
|
||||
// account for inaccurate reporting by rounding up (see wposix sysconf)
|
||||
@@ -147,16 +95,59 @@ static size_t DetermineMemoryTotalMiB()
|
||||
else
|
||||
memory_total = memory_total_pow2;
|
||||
|
||||
const size_t memory_total_mib = memory_total / MiB;
|
||||
return memory_total_mib;
|
||||
memory_total_mib = memory_total / MiB;
|
||||
}
|
||||
|
||||
|
||||
bool cpu_IsModuleInitialized()
|
||||
{
|
||||
return module_init_state == MODULE_INITIALIZED;
|
||||
}
|
||||
|
||||
double cpu_ClockFrequency()
|
||||
{
|
||||
return clock_frequency;
|
||||
}
|
||||
|
||||
bool cpu_IsThrottlingPossible()
|
||||
{
|
||||
return is_throttling_possible;
|
||||
}
|
||||
|
||||
size_t cpu_MemoryTotalMiB()
|
||||
{
|
||||
return cpu_memory_total_mib;
|
||||
return memory_total_mib;
|
||||
}
|
||||
|
||||
const char* cpu_IdentifierString()
|
||||
{
|
||||
#if CPU_IA32
|
||||
return ia32_IdentifierString(); // cached
|
||||
#endif
|
||||
}
|
||||
|
||||
uint cpu_NumPackages()
|
||||
{
|
||||
#if CPU_IA32
|
||||
return ia32_NumPackages(); // cached
|
||||
#endif
|
||||
}
|
||||
|
||||
uint cpu_CoresPerPackage()
|
||||
{
|
||||
#if CPU_IA32
|
||||
return ia32_CoresPerPackage(); // cached
|
||||
#endif
|
||||
}
|
||||
|
||||
uint cpu_LogicalPerCore()
|
||||
{
|
||||
#if CPU_IA32
|
||||
return ia32_LogicalPerCore(); // cached
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if CPU_IA32
|
||||
@@ -195,16 +186,16 @@ void cpu_Init()
|
||||
InitAndConfigureIA32();
|
||||
#endif
|
||||
|
||||
// memory
|
||||
cpu_page_size = (size_t)sysconf(_SC_PAGESIZE);
|
||||
cpu_memory_total_mib = DetermineMemoryTotalMiB();
|
||||
DetectMemory();
|
||||
DetectIfThrottlingPossible();
|
||||
DetectClockFrequency();
|
||||
|
||||
// must be set before wtime_reset_impl since it queries this flag via
|
||||
// cpu_IsModuleInitialized.
|
||||
module_init_state = MODULE_INITIALIZED;
|
||||
|
||||
// HACK: on Windows, the HRT makes its final implementation choice
|
||||
// in the first calibrate call where cpu info is available.
|
||||
// in the first calibrate call where CPU info is available.
|
||||
// call wtime_reset_impl here to have that happen now so app code isn't
|
||||
// surprised by a timer change, although the HRT does try to
|
||||
// keep the timer continuous.
|
||||
@@ -215,6 +206,7 @@ void cpu_Init()
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// stateless routines
|
||||
|
||||
bool cpu_CAS(uintptr_t* location, uintptr_t expected, uintptr_t new_value)
|
||||
{
|
||||
@@ -287,3 +279,34 @@ i64 cpu_i64FromDouble(double d)
|
||||
return (i64)d;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// System V derived (GNU/Linux, Solaris)
|
||||
#if defined(_SC_AVPHYS_PAGES)
|
||||
|
||||
static int SysconfFromMemType(CpuMemoryIndicators mem_type)
|
||||
{
|
||||
switch(mem_type)
|
||||
{
|
||||
case CPU_MEM_TOTAL:
|
||||
return _SC_PHYS_PAGES;
|
||||
case CPU_MEM_AVAILABLE:
|
||||
return _SC_AVPHYS_PAGES;
|
||||
}
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
size_t cpu_MemorySize(CpuMemoryIndicators mem_type)
|
||||
{
|
||||
// quasi-POSIX
|
||||
#if defined(_SC_AVPHYS_PAGES)
|
||||
const int sc_name = SysconfFromMemType(mem_type);
|
||||
const size_t memory_size = sysconf(sc_name) * page_size;
|
||||
return memory_size;
|
||||
// BSD / Mac OS X
|
||||
#else
|
||||
return bsd_MemorySize(mem_type);
|
||||
#endif
|
||||
}
|
||||
+11
-17
@@ -28,26 +28,12 @@ namespace ERR
|
||||
extern void cpu_Init(void);
|
||||
|
||||
extern bool cpu_IsModuleInitialized();
|
||||
|
||||
|
||||
extern const char* cpu_IdentifierString();
|
||||
extern double cpu_ClockFrequency();
|
||||
extern bool cpu_IsThrottlingPossible();
|
||||
extern uint cpu_NumPackages(); // i.e. sockets
|
||||
extern uint cpu_CoresPerPackage();
|
||||
extern uint cpu_LogicalPerCore();
|
||||
extern bool cpu_IsThrottlingPossible();
|
||||
|
||||
|
||||
//
|
||||
// memory
|
||||
//
|
||||
|
||||
enum CpuMemoryIndicators
|
||||
{
|
||||
CPU_MEM_TOTAL, CPU_MEM_AVAILABLE
|
||||
};
|
||||
|
||||
extern size_t cpu_MemorySize(CpuMemoryIndicators mem_type);
|
||||
|
||||
// faster than cpu_MemorySize (caches total size determined during init),
|
||||
// returns #Mebibytes (cleaned up to account e.g. for nonpaged pool)
|
||||
@@ -55,7 +41,7 @@ extern size_t cpu_MemoryTotalMiB();
|
||||
|
||||
|
||||
//
|
||||
// misc
|
||||
// misc (stateless)
|
||||
//
|
||||
|
||||
// atomic "compare and swap". compare the machine word at <location> against
|
||||
@@ -75,10 +61,18 @@ extern bool cpu_CAS(uintptr_t* location, uintptr_t expected, uintptr_t new_value
|
||||
**/
|
||||
extern void cpu_AtomicAdd(intptr_t* location, intptr_t increment);
|
||||
|
||||
extern void cpu_Serialize();
|
||||
|
||||
// enforce strong memory ordering.
|
||||
extern void cpu_MemoryFence();
|
||||
|
||||
extern void cpu_Serialize();
|
||||
|
||||
enum CpuMemoryIndicators
|
||||
{
|
||||
CPU_MEM_TOTAL, CPU_MEM_AVAILABLE
|
||||
};
|
||||
|
||||
extern size_t cpu_MemorySize(CpuMemoryIndicators mem_type);
|
||||
|
||||
|
||||
// drop-in replacement for libc memcpy(). only requires CPU support for
|
||||
|
||||
@@ -208,7 +208,11 @@ public:
|
||||
const char* ia32_IdentifierString()
|
||||
{
|
||||
// 3 calls x 4 registers x 4 bytes = 48
|
||||
static char identifier_string[48+1] = "";
|
||||
static char identifier_string[48+1] = {'\0'};
|
||||
|
||||
// not first call, return previous result
|
||||
if(identifier_string[0] != '\0')
|
||||
return identifier_string;
|
||||
|
||||
// get processor signature
|
||||
u32 regs[4];
|
||||
@@ -309,92 +313,106 @@ int ia32_IsThrottlingPossible()
|
||||
}
|
||||
|
||||
|
||||
// set scheduling priority and restore when going out of scope.
|
||||
class ScopedSetPriority
|
||||
{
|
||||
int m_old_policy;
|
||||
sched_param m_old_param;
|
||||
|
||||
public:
|
||||
ScopedSetPriority(int new_priority)
|
||||
{
|
||||
// get current scheduling policy and priority
|
||||
pthread_getschedparam(pthread_self(), &m_old_policy, &m_old_param);
|
||||
|
||||
// set new priority
|
||||
sched_param new_param = {0};
|
||||
new_param.sched_priority = new_priority;
|
||||
pthread_setschedparam(pthread_self(), SCHED_FIFO, &new_param);
|
||||
}
|
||||
|
||||
~ScopedSetPriority()
|
||||
{
|
||||
// restore previous policy and priority.
|
||||
pthread_setschedparam(pthread_self(), m_old_policy, &m_old_param);
|
||||
}
|
||||
};
|
||||
|
||||
double ia32_ClockFrequency()
|
||||
{
|
||||
double clock_frequency = 0.0;
|
||||
// if the TSC isn't available, there's really no good way to count the
|
||||
// actual CPU clocks per known time interval, so bail.
|
||||
// note: loop iterations ("bogomips") are not a reliable measure due
|
||||
// to differing IPC and compiler optimizations.
|
||||
if(!ia32_cap(IA32_CAP_TSC))
|
||||
return -1.0; // impossible value
|
||||
|
||||
// set max priority, to reduce interference while measuring.
|
||||
int old_policy; static sched_param old_param; // (static => 0-init)
|
||||
pthread_getschedparam(pthread_self(), &old_policy, &old_param);
|
||||
static sched_param max_param;
|
||||
max_param.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
||||
pthread_setschedparam(pthread_self(), SCHED_FIFO, &max_param);
|
||||
// increase priority to reduce interference while measuring.
|
||||
const int priority = sched_get_priority_max(SCHED_FIFO)-1;
|
||||
ScopedSetPriority ssp(priority);
|
||||
|
||||
// make sure the TSC is available, because we're going to
|
||||
// measure actual CPU clocks per known time interval.
|
||||
// counting loop iterations ("bogomips") is unreliable.
|
||||
if(ia32_cap(IA32_CAP_TSC))
|
||||
// note: no need to "warm up" cpuid - it will already have been
|
||||
// called several times by the time this code is reached.
|
||||
// (background: it's used in ia32_rdtsc() to serialize instruction flow;
|
||||
// the first call is documented to be slower on Intel CPUs)
|
||||
|
||||
int num_samples = 16;
|
||||
// if clock is low-res, do less samples so it doesn't take too long.
|
||||
// balance measuring time (~ 10 ms) and accuracy (< 1 0/00 error -
|
||||
// ok for using the TSC as a time reference)
|
||||
if(timer_res() >= 1e-3)
|
||||
num_samples = 8;
|
||||
std::vector<double> samples(num_samples);
|
||||
|
||||
for(int i = 0; i < num_samples; i++)
|
||||
{
|
||||
// note: no need to "warm up" cpuid - it will already have been
|
||||
// called several times by the time this code is reached.
|
||||
// (background: it's used in ia32_rdtsc() to serialize instruction flow;
|
||||
// the first call is documented to be slower on Intel CPUs)
|
||||
double dt;
|
||||
i64 dc; // i64 because VC6 can't convert u64 -> double,
|
||||
// and we don't need all 64 bits.
|
||||
|
||||
int num_samples = 16;
|
||||
// if clock is low-res, do less samples so it doesn't take too long.
|
||||
// balance measuring time (~ 10 ms) and accuracy (< 1 0/00 error -
|
||||
// ok for using the TSC as a time reference)
|
||||
if(timer_res() >= 1e-3)
|
||||
num_samples = 8;
|
||||
std::vector<double> samples(num_samples);
|
||||
|
||||
int i;
|
||||
for(i = 0; i < num_samples; i++)
|
||||
// count # of clocks in max{1 tick, 1 ms}:
|
||||
// .. wait for start of tick.
|
||||
const double t0 = get_time();
|
||||
u64 c1; double t1;
|
||||
do
|
||||
{
|
||||
double dt;
|
||||
i64 dc;
|
||||
// i64 because VC6 can't convert u64 -> double,
|
||||
// and we don't need all 64 bits.
|
||||
|
||||
// count # of clocks in max{1 tick, 1 ms}:
|
||||
// .. wait for start of tick.
|
||||
const double t0 = get_time();
|
||||
u64 c1; double t1;
|
||||
do
|
||||
{
|
||||
// note: get_time effectively has a long delay (up to 5 us)
|
||||
// before returning the time. we call it before ia32_rdtsc to
|
||||
// minimize the delay between actually sampling time / TSC,
|
||||
// thus decreasing the chance for interference.
|
||||
// (if unavoidable background activity, e.g. interrupts,
|
||||
// delays the second reading, inaccuracy is introduced).
|
||||
t1 = get_time();
|
||||
c1 = ia32_rdtsc();
|
||||
}
|
||||
while(t1 == t0);
|
||||
// .. wait until start of next tick and at least 1 ms elapsed.
|
||||
do
|
||||
{
|
||||
const double t2 = get_time();
|
||||
const u64 c2 = ia32_rdtsc();
|
||||
dc = (i64)(c2 - c1);
|
||||
dt = t2 - t1;
|
||||
}
|
||||
while(dt < 1e-3);
|
||||
|
||||
// .. freq = (delta_clocks) / (delta_seconds);
|
||||
// ia32_rdtsc/timer overhead is negligible.
|
||||
const double freq = dc / dt;
|
||||
samples[i] = freq;
|
||||
// note: get_time effectively has a long delay (up to 5 us)
|
||||
// before returning the time. we call it before ia32_rdtsc to
|
||||
// minimize the delay between actually sampling time / TSC,
|
||||
// thus decreasing the chance for interference.
|
||||
// (if unavoidable background activity, e.g. interrupts,
|
||||
// delays the second reading, inaccuracy is introduced).
|
||||
t1 = get_time();
|
||||
c1 = ia32_rdtsc();
|
||||
}
|
||||
while(t1 == t0);
|
||||
// .. wait until start of next tick and at least 1 ms elapsed.
|
||||
do
|
||||
{
|
||||
const double t2 = get_time();
|
||||
const u64 c2 = ia32_rdtsc();
|
||||
dc = (i64)(c2 - c1);
|
||||
dt = t2 - t1;
|
||||
}
|
||||
while(dt < 1e-3);
|
||||
|
||||
std::sort(samples.begin(), samples.end());
|
||||
|
||||
// median filter (remove upper and lower 25% and average the rest).
|
||||
// note: don't just take the lowest value! it could conceivably be
|
||||
// too low, if background processing delays reading c1 (see above).
|
||||
double sum = 0.0;
|
||||
const int lo = num_samples/4, hi = 3*num_samples/4;
|
||||
for(i = lo; i < hi; i++)
|
||||
sum += samples[i];
|
||||
clock_frequency = sum / (hi-lo);
|
||||
|
||||
// .. freq = (delta_clocks) / (delta_seconds);
|
||||
// ia32_rdtsc/timer overhead is negligible.
|
||||
const double freq = dc / dt;
|
||||
samples[i] = freq;
|
||||
}
|
||||
// else: TSC not available, can't measure; cpu_freq remains unchanged.
|
||||
|
||||
// restore previous policy and priority.
|
||||
pthread_setschedparam(pthread_self(), old_policy, &old_param);
|
||||
std::sort(samples.begin(), samples.end());
|
||||
|
||||
// median filter (remove upper and lower 25% and average the rest).
|
||||
// note: don't just take the lowest value! it could conceivably be
|
||||
// too low, if background processing delays reading c1 (see above).
|
||||
double sum = 0.0;
|
||||
const int lo = num_samples/4, hi = 3*num_samples/4;
|
||||
for(int i = lo; i < hi; i++)
|
||||
sum += samples[i];
|
||||
|
||||
const double clock_frequency = sum / (hi-lo);
|
||||
return clock_frequency;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ AT_STARTUP(\
|
||||
// (default values for HRT_NONE impl)
|
||||
|
||||
// initial measurement of the time source's tick rate. not necessarily
|
||||
// correct (e.g. when using TSC; cpu_ClockFrequency isn't exact).
|
||||
// correct (e.g. when using TSC: cpu_ClockFrequency isn't exact).
|
||||
static double hrt_nominal_freq = -1.0;
|
||||
|
||||
// actual resolution of the time source (may differ from hrt_nominal_freq
|
||||
@@ -130,9 +130,9 @@ enum HRTOverride
|
||||
HRT_FORCE
|
||||
};
|
||||
|
||||
// HRTImpl enums as index
|
||||
// HACK: no init needed - static data is zeroed (= HRT_DEFAULT)
|
||||
static HRTOverride overrides[HRT_NUM_IMPLS];
|
||||
// HRTImpl enums as index
|
||||
// HACK: no init needed - static data is zeroed (= HRT_DEFAULT)
|
||||
cassert((int)HRT_DEFAULT == 0);
|
||||
|
||||
|
||||
@@ -152,6 +152,14 @@ static inline void unlock(void)
|
||||
}
|
||||
|
||||
|
||||
static bool IsSimilarMagnitude(double d1, double d2, const double relative_error_tolerance = 0.05)
|
||||
{
|
||||
const double relative_error = fabs(d1/d2 - 1.0);
|
||||
if(relative_error > relative_error_tolerance)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// decide upon a HRT implementation, checking if we can work around
|
||||
// each timer's issues on this platform, but allow user override
|
||||
// in case there are unforeseen problems with one of them.
|
||||
@@ -166,6 +174,9 @@ static LibError choose_impl()
|
||||
if(overrides[impl] == HRT_FORCE)\
|
||||
safe = true;
|
||||
|
||||
// used several times below, so latch it for convenience.
|
||||
const double cpu_freq = cpu_IsModuleInitialized()? cpu_ClockFrequency() : 0.0;
|
||||
|
||||
#if CPU_IA32 && !defined(NO_TSC)
|
||||
// CPU Timestamp Counter (incremented every clock)
|
||||
// ns resolution, moderate precision (poor clock crystal?)
|
||||
@@ -187,7 +198,62 @@ static LibError choose_impl()
|
||||
// will do this as well (if not to save power, for heat reasons).
|
||||
// frequency changes are too often and drastic to correct,
|
||||
// and we don't want to mess with the system power settings => unsafe.
|
||||
if(cpu_IsModuleInitialized() && cpu_ClockFrequency() > 0.0 && ia32_cap(IA32_CAP_TSC))
|
||||
|
||||
|
||||
/*
|
||||
AMD has defined a CPUID feature bit that
|
||||
software can test to determine if the TSC is
|
||||
invariant. Issuing a CPUID instruction with an %eax register
|
||||
value of 0x8000_0007, on a processor whose base family is
|
||||
0xF, returns "Advanced Power Management Information" in the
|
||||
%eax, %ebx, %ecx, and %edx registers. Bit 8 of the return
|
||||
%edx is the "TscInvariant" feature flag which is set when
|
||||
TSC is P-state, C-state, and STPCLK-throttling invariant; it
|
||||
is clear otherwise.
|
||||
*/
|
||||
|
||||
/*
|
||||
if (CPUID.base_family < 0xf) {
|
||||
// TSC drift doesn't exist on 7th Gen or less
|
||||
// However, OS still needs to consider effects
|
||||
// of P-state changes on TSC
|
||||
return TRUE;
|
||||
|
||||
} else if (CPUID.AdvPowerMgmtInfo.TscInvariant) {
|
||||
// Invariant TSC on 8th Gen or newer, use it
|
||||
// (assume all cores have invariant TSC)
|
||||
return TRUE;
|
||||
|
||||
} else if ((number_processors == 1)&&(number_cores == 1)){
|
||||
// OK to use TSC on uni-processor-uni-core
|
||||
// However, OS still needs to consider effects
|
||||
// of P-state changes on TSC
|
||||
return TRUE;
|
||||
|
||||
} else if ( (number_processors == 1) &&
|
||||
(CPUID.effective_family == 0x0f) &&
|
||||
!C1_ramp_8gen ){
|
||||
// Use TSC on 8th Gen uni-proc with C1_ramp off
|
||||
// However, OS still needs to consider effects
|
||||
// of P-state changes on TSC
|
||||
return TRUE;
|
||||
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
C1_ramp_8gen() {
|
||||
// Check if C1-Clock ramping enabled in PMM7.CpuLowPwrEnh
|
||||
// On 8th-Generation cores only. Assume BIOS has setup
|
||||
// all Northbridges equivalently.
|
||||
|
||||
return (1 & read_pci_byte(bus=0,dev=0x18,fcn=3,offset=0x87));
|
||||
}
|
||||
*/
|
||||
|
||||
if(cpu_freq > 0.0 && ia32_cap(IA32_CAP_TSC))
|
||||
{
|
||||
safe = (cpu_CoresPerPackage() == 1 && cpu_NumPackages() == 1 && cpu_IsThrottlingPossible() == 0);
|
||||
SAFETY_OVERRIDE(HRT_TSC);
|
||||
@@ -213,17 +279,15 @@ static LibError choose_impl()
|
||||
// 2) "System clock problem can inflate benchmark scores":
|
||||
// incorrect value if not polled every 4.5 seconds? solved
|
||||
// by calibration thread, which reads timer every second anyway.
|
||||
// - TSC on MP HAL - see TSC above.
|
||||
// - TSC on MP HAL, sometimes with 1/3 of CPU freq.
|
||||
|
||||
// cache freq because QPF is fairly slow.
|
||||
static i64 qpc_freq = -1;
|
||||
|
||||
// first call - check if QPC is supported
|
||||
if(qpc_freq == -1)
|
||||
static i64 qpc_freq = -1; // set to 0 if unsupported
|
||||
if(qpc_freq == -1) // first call
|
||||
{
|
||||
LARGE_INTEGER i;
|
||||
BOOL qpc_ok = QueryPerformanceFrequency(&i);
|
||||
qpc_freq = qpc_ok? i.QuadPart : 0;
|
||||
LARGE_INTEGER freq;
|
||||
BOOL qpc_ok = QueryPerformanceFrequency(&freq);
|
||||
qpc_freq = qpc_ok? freq.QuadPart : 0;
|
||||
}
|
||||
|
||||
// QPC is available
|
||||
@@ -241,11 +305,14 @@ static LibError choose_impl()
|
||||
safe = false;
|
||||
else
|
||||
{
|
||||
// compare QPC freq to CPU clock freq - can't rule out HPET,
|
||||
// because its frequency isn't known (it's at least 10 MHz).
|
||||
double freq_dist = fabs(cpu_ClockFrequency()/qpc_freq - 1.0);
|
||||
safe = freq_dist > 0.05;
|
||||
// safe if freqs not within 5% (i.e. it doesn't use TSC)
|
||||
safe = true;
|
||||
// compare QPC freq to CPU clock freq. note: we can't
|
||||
// single out the HPET (as with PIT and PMT above) because
|
||||
// its frequency is variable and at least 10 MHz.
|
||||
if(IsSimilarMagnitude(qpc_freq, cpu_freq))
|
||||
safe = false;
|
||||
if(IsSimilarMagnitude(qpc_freq, cpu_freq/3)) // QPC sometimes uses RDTSC/3
|
||||
safe = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,12 +385,8 @@ static i64 ticks_lk()
|
||||
|
||||
// add further timers here.
|
||||
|
||||
case HRT_NUM_IMPLS:
|
||||
default:
|
||||
debug_warn("invalid impl");
|
||||
//-fallthrough
|
||||
|
||||
case HRT_NONE:
|
||||
return 0;
|
||||
} // switch(impl)
|
||||
}
|
||||
@@ -348,7 +411,7 @@ static double time_lk()
|
||||
|
||||
|
||||
|
||||
// this module is dependent upon detect (supplies system information needed to
|
||||
// this module is dependent upon cpu.cpp (supplies information needed to
|
||||
// choose a HRT), which in turn uses our timer to detect the CPU clock
|
||||
// when running on Windows (clock(), the only cross platform HRT available on
|
||||
// Windows, isn't good enough - only 10..15 ms resolution).
|
||||
@@ -390,6 +453,8 @@ static LibError reset_impl_lk()
|
||||
hrt_cal_ticks = ticks_lk();
|
||||
}
|
||||
|
||||
debug_printf("HRT impl=%d nominal_freq=%f cur_freq=%f\n", hrt_impl, hrt_nominal_freq, hrt_cur_freq);
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
@@ -408,9 +473,8 @@ unlock();
|
||||
// return seconds since init.
|
||||
static double hrt_time()
|
||||
{
|
||||
double t;
|
||||
lock();
|
||||
t = time_lk();
|
||||
const double t = time_lk();
|
||||
unlock();
|
||||
return t;
|
||||
}
|
||||
@@ -423,7 +487,7 @@ static double hrt_delta_s(i64 start, i64 end)
|
||||
{
|
||||
// paranoia: reading double may not be atomic.
|
||||
lock();
|
||||
double freq = hrt_cur_freq;
|
||||
const double freq = hrt_cur_freq;
|
||||
unlock();
|
||||
|
||||
debug_assert(freq != -1.0 && "hrt_delta_s: hrt_cur_freq not set");
|
||||
@@ -434,8 +498,6 @@ unlock();
|
||||
// return current timer implementation and its nominal (rated) frequency.
|
||||
// nominal_freq is never 0.
|
||||
// implementation only changes after hrt_override_impl.
|
||||
//
|
||||
// may be called before first hrt_ticks / hrt_time, so do init here also.
|
||||
static void hrt_query_impl(HRTImpl& impl, double& nominal_freq, double& res)
|
||||
{
|
||||
lock();
|
||||
@@ -472,12 +534,9 @@ unlock();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// calibration
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// 'safe' timer, used to measure HRT freq in calibrate()
|
||||
static const long safe_timer_freq = 1000;
|
||||
@@ -629,11 +688,9 @@ static LibError hrt_shutdown()
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// wtime wrapper: emulates POSIX functions
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// NT system time and FILETIME are hectonanoseconds since Jan. 1, 1601 UTC.
|
||||
// SYSTEMTIME is a struct containing month, year, etc.
|
||||
|
||||
@@ -129,7 +129,7 @@ static char ds_drv_path[MAX_PATH+1];
|
||||
// store sound card name and path to DirectSound driver.
|
||||
// called for each DirectSound driver, but aborts after first valid driver.
|
||||
static BOOL CALLBACK ds_enum(void* UNUSED(guid), const char* description,
|
||||
const char* module, void* UNUSED(ctx))
|
||||
const char* module, void* UNUSED(ctx))
|
||||
{
|
||||
// skip first (dummy) entry, where description == "Primary Sound Driver".
|
||||
if(module[0] == '\0')
|
||||
|
||||
@@ -58,7 +58,7 @@ double get_time()
|
||||
// make sure time is monotonic (never goes backwards)
|
||||
static double t_last = 0.0;
|
||||
if(t < t_last)
|
||||
t = t_last;
|
||||
t = t_last+DBL_EPSILON;
|
||||
t_last = t;
|
||||
|
||||
return t;
|
||||
|
||||
Reference in New Issue
Block a user