Browse Source

Merge pull request #8543 from BreadFish64/use_tsc_from_caps

common/x64: Use TSC clock rate from CPUID when available
bunnei 4 years ago
parent
commit
8ca8281f4f
3 changed files with 22 additions and 1 deletions
  1. 1 1
      src/common/wall_clock.cpp
  2. 16 0
      src/common/x64/cpu_detect.cpp
  3. 5 0
      src/common/x64/cpu_detect.h

+ 1 - 1
src/common/wall_clock.cpp

@@ -67,7 +67,7 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
     const auto& caps = GetCPUCaps();
     u64 rtsc_frequency = 0;
     if (caps.invariant_tsc) {
-        rtsc_frequency = EstimateRDTSCFrequency();
+        rtsc_frequency = caps.tsc_frequency ? caps.tsc_frequency : EstimateRDTSCFrequency();
     }
 
     // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than:

+ 16 - 0
src/common/x64/cpu_detect.cpp

@@ -161,6 +161,22 @@ static CPUCaps Detect() {
         caps.invariant_tsc = Common::Bit<8>(cpu_id[3]);
     }
 
+    if (max_std_fn >= 0x15) {
+        __cpuid(cpu_id, 0x15);
+        caps.tsc_crystal_ratio_denominator = cpu_id[0];
+        caps.tsc_crystal_ratio_numerator = cpu_id[1];
+        caps.crystal_frequency = cpu_id[2];
+        // Some CPU models might not return a crystal frequency.
+        // The CPU model can be detected to use the values from turbostat
+        // https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c#L5569
+        // but it's easier to just estimate the TSC tick rate for these cases.
+        if (caps.tsc_crystal_ratio_denominator) {
+            caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) *
+                                 caps.tsc_crystal_ratio_numerator /
+                                 caps.tsc_crystal_ratio_denominator;
+        }
+    }
+
     if (max_std_fn >= 0x16) {
         __cpuid(cpu_id, 0x16);
         caps.base_frequency = cpu_id[0];

+ 5 - 0
src/common/x64/cpu_detect.h

@@ -30,6 +30,11 @@ struct CPUCaps {
     u32 max_frequency;
     u32 bus_frequency;
 
+    u32 tsc_crystal_ratio_denominator;
+    u32 tsc_crystal_ratio_numerator;
+    u32 crystal_frequency;
+    u64 tsc_frequency; // Derived from the above three values
+
     bool sse : 1;
     bool sse2 : 1;
     bool sse3 : 1;