diff options
Diffstat (limited to 'src/common/x64')
| -rw-r--r-- | src/common/x64/cpu_detect.cpp | 3 | ||||
| -rw-r--r-- | src/common/x64/cpu_wait.cpp | 20 | ||||
| -rw-r--r-- | src/common/x64/native_clock.cpp | 166 | ||||
| -rw-r--r-- | src/common/x64/native_clock.h | 59 | ||||
| -rw-r--r-- | src/common/x64/rdtsc.cpp | 39 | ||||
| -rw-r--r-- | src/common/x64/rdtsc.h | 37 |
6 files changed, 126 insertions, 198 deletions
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index 72ed6e96c..c998b1197 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 15 | #include "common/logging/log.h" | 15 | #include "common/logging/log.h" |
| 16 | #include "common/x64/cpu_detect.h" | 16 | #include "common/x64/cpu_detect.h" |
| 17 | #include "common/x64/rdtsc.h" | ||
| 17 | 18 | ||
| 18 | #ifdef _WIN32 | 19 | #ifdef _WIN32 |
| 19 | #include <windows.h> | 20 | #include <windows.h> |
| @@ -187,6 +188,8 @@ static CPUCaps Detect() { | |||
| 187 | caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) * | 188 | caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) * |
| 188 | caps.tsc_crystal_ratio_numerator / | 189 | caps.tsc_crystal_ratio_numerator / |
| 189 | caps.tsc_crystal_ratio_denominator; | 190 | caps.tsc_crystal_ratio_denominator; |
| 191 | } else { | ||
| 192 | caps.tsc_frequency = X64::EstimateRDTSCFrequency(); | ||
| 190 | } | 193 | } |
| 191 | } | 194 | } |
| 192 | 195 | ||
diff --git a/src/common/x64/cpu_wait.cpp b/src/common/x64/cpu_wait.cpp index cfeef6a3d..c53dd4945 100644 --- a/src/common/x64/cpu_wait.cpp +++ b/src/common/x64/cpu_wait.cpp | |||
| @@ -9,19 +9,11 @@ | |||
| 9 | 9 | ||
| 10 | #include "common/x64/cpu_detect.h" | 10 | #include "common/x64/cpu_detect.h" |
| 11 | #include "common/x64/cpu_wait.h" | 11 | #include "common/x64/cpu_wait.h" |
| 12 | #include "common/x64/rdtsc.h" | ||
| 12 | 13 | ||
| 13 | namespace Common::X64 { | 14 | namespace Common::X64 { |
| 14 | 15 | ||
| 15 | #ifdef _MSC_VER | 16 | #ifdef _MSC_VER |
| 16 | __forceinline static u64 FencedRDTSC() { | ||
| 17 | _mm_lfence(); | ||
| 18 | _ReadWriteBarrier(); | ||
| 19 | const u64 result = __rdtsc(); | ||
| 20 | _mm_lfence(); | ||
| 21 | _ReadWriteBarrier(); | ||
| 22 | return result; | ||
| 23 | } | ||
| 24 | |||
| 25 | __forceinline static void TPAUSE() { | 17 | __forceinline static void TPAUSE() { |
| 26 | // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. | 18 | // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. |
| 27 | // For reference: | 19 | // For reference: |
| @@ -32,16 +24,6 @@ __forceinline static void TPAUSE() { | |||
| 32 | _tpause(0, FencedRDTSC() + PauseCycles); | 24 | _tpause(0, FencedRDTSC() + PauseCycles); |
| 33 | } | 25 | } |
| 34 | #else | 26 | #else |
| 35 | static u64 FencedRDTSC() { | ||
| 36 | u64 eax; | ||
| 37 | u64 edx; | ||
| 38 | asm volatile("lfence\n\t" | ||
| 39 | "rdtsc\n\t" | ||
| 40 | "lfence\n\t" | ||
| 41 | : "=a"(eax), "=d"(edx)); | ||
| 42 | return (edx << 32) | eax; | ||
| 43 | } | ||
| 44 | |||
| 45 | static void TPAUSE() { | 27 | static void TPAUSE() { |
| 46 | // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. | 28 | // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources. |
| 47 | // For reference: | 29 | // For reference: |
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 277b00662..7d2a26bd9 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp | |||
| @@ -1,164 +1,50 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <array> | ||
| 5 | #include <chrono> | ||
| 6 | #include <thread> | ||
| 7 | |||
| 8 | #include "common/atomic_ops.h" | ||
| 9 | #include "common/steady_clock.h" | ||
| 10 | #include "common/uint128.h" | 4 | #include "common/uint128.h" |
| 11 | #include "common/x64/native_clock.h" | 5 | #include "common/x64/native_clock.h" |
| 6 | #include "common/x64/rdtsc.h" | ||
| 12 | 7 | ||
| 13 | #ifdef _MSC_VER | 8 | namespace Common::X64 { |
| 14 | #include <intrin.h> | ||
| 15 | #endif | ||
| 16 | |||
| 17 | namespace Common { | ||
| 18 | 9 | ||
| 19 | #ifdef _MSC_VER | 10 | NativeClock::NativeClock(u64 rdtsc_frequency_) |
| 20 | __forceinline static u64 FencedRDTSC() { | 11 | : start_ticks{FencedRDTSC()}, rdtsc_frequency{rdtsc_frequency_}, |
| 21 | _mm_lfence(); | 12 | ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency)}, |
| 22 | _ReadWriteBarrier(); | 13 | us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)}, |
| 23 | const u64 result = __rdtsc(); | 14 | ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)}, |
| 24 | _mm_lfence(); | 15 | cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)}, |
| 25 | _ReadWriteBarrier(); | 16 | gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {} |
| 26 | return result; | ||
| 27 | } | ||
| 28 | #else | ||
| 29 | static u64 FencedRDTSC() { | ||
| 30 | u64 eax; | ||
| 31 | u64 edx; | ||
| 32 | asm volatile("lfence\n\t" | ||
| 33 | "rdtsc\n\t" | ||
| 34 | "lfence\n\t" | ||
| 35 | : "=a"(eax), "=d"(edx)); | ||
| 36 | return (edx << 32) | eax; | ||
| 37 | } | ||
| 38 | #endif | ||
| 39 | 17 | ||
| 40 | template <u64 Nearest> | 18 | std::chrono::nanoseconds NativeClock::GetTimeNS() const { |
| 41 | static u64 RoundToNearest(u64 value) { | 19 | return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_rdtsc_factor)}; |
| 42 | const auto mod = value % Nearest; | ||
| 43 | return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod); | ||
| 44 | } | 20 | } |
| 45 | 21 | ||
| 46 | u64 EstimateRDTSCFrequency() { | 22 | std::chrono::microseconds NativeClock::GetTimeUS() const { |
| 47 | // Discard the first result measuring the rdtsc. | 23 | return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_rdtsc_factor)}; |
| 48 | FencedRDTSC(); | ||
| 49 | std::this_thread::sleep_for(std::chrono::milliseconds{1}); | ||
| 50 | FencedRDTSC(); | ||
| 51 | |||
| 52 | // Get the current time. | ||
| 53 | const auto start_time = Common::RealTimeClock::Now(); | ||
| 54 | const u64 tsc_start = FencedRDTSC(); | ||
| 55 | // Wait for 250 milliseconds. | ||
| 56 | std::this_thread::sleep_for(std::chrono::milliseconds{250}); | ||
| 57 | const auto end_time = Common::RealTimeClock::Now(); | ||
| 58 | const u64 tsc_end = FencedRDTSC(); | ||
| 59 | // Calculate differences. | ||
| 60 | const u64 timer_diff = static_cast<u64>( | ||
| 61 | std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); | ||
| 62 | const u64 tsc_diff = tsc_end - tsc_start; | ||
| 63 | const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); | ||
| 64 | return RoundToNearest<1000>(tsc_freq); | ||
| 65 | } | 24 | } |
| 66 | 25 | ||
| 67 | namespace X64 { | 26 | std::chrono::milliseconds NativeClock::GetTimeMS() const { |
| 68 | NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, | 27 | return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_rdtsc_factor)}; |
| 69 | u64 rtsc_frequency_) | ||
| 70 | : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{ | ||
| 71 | rtsc_frequency_} { | ||
| 72 | // Thread to re-adjust the RDTSC frequency after 10 seconds has elapsed. | ||
| 73 | time_sync_thread = std::jthread{[this](std::stop_token token) { | ||
| 74 | // Get the current time. | ||
| 75 | const auto start_time = Common::RealTimeClock::Now(); | ||
| 76 | const u64 tsc_start = FencedRDTSC(); | ||
| 77 | // Wait for 10 seconds. | ||
| 78 | if (!Common::StoppableTimedWait(token, std::chrono::seconds{10})) { | ||
| 79 | return; | ||
| 80 | } | ||
| 81 | const auto end_time = Common::RealTimeClock::Now(); | ||
| 82 | const u64 tsc_end = FencedRDTSC(); | ||
| 83 | // Calculate differences. | ||
| 84 | const u64 timer_diff = static_cast<u64>( | ||
| 85 | std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); | ||
| 86 | const u64 tsc_diff = tsc_end - tsc_start; | ||
| 87 | const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); | ||
| 88 | rtsc_frequency = tsc_freq; | ||
| 89 | CalculateAndSetFactors(); | ||
| 90 | }}; | ||
| 91 | |||
| 92 | time_point.inner.last_measure = FencedRDTSC(); | ||
| 93 | time_point.inner.accumulated_ticks = 0U; | ||
| 94 | CalculateAndSetFactors(); | ||
| 95 | } | 28 | } |
| 96 | 29 | ||
| 97 | u64 NativeClock::GetRTSC() { | 30 | u64 NativeClock::GetCNTPCT() const { |
| 98 | TimePoint new_time_point{}; | 31 | return MultiplyHigh(GetHostTicksElapsed(), cntpct_rdtsc_factor); |
| 99 | TimePoint current_time_point{}; | ||
| 100 | |||
| 101 | current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); | ||
| 102 | do { | ||
| 103 | const u64 current_measure = FencedRDTSC(); | ||
| 104 | u64 diff = current_measure - current_time_point.inner.last_measure; | ||
| 105 | diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) | ||
| 106 | new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure | ||
| 107 | ? current_measure | ||
| 108 | : current_time_point.inner.last_measure; | ||
| 109 | new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; | ||
| 110 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, | ||
| 111 | current_time_point.pack, current_time_point.pack)); | ||
| 112 | return new_time_point.inner.accumulated_ticks; | ||
| 113 | } | 32 | } |
| 114 | 33 | ||
| 115 | void NativeClock::Pause(bool is_paused) { | 34 | u64 NativeClock::GetGPUTick() const { |
| 116 | if (!is_paused) { | 35 | return MultiplyHigh(GetHostTicksElapsed(), gputick_rdtsc_factor); |
| 117 | TimePoint current_time_point{}; | ||
| 118 | TimePoint new_time_point{}; | ||
| 119 | |||
| 120 | current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); | ||
| 121 | do { | ||
| 122 | new_time_point.pack = current_time_point.pack; | ||
| 123 | new_time_point.inner.last_measure = FencedRDTSC(); | ||
| 124 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, | ||
| 125 | current_time_point.pack, current_time_point.pack)); | ||
| 126 | } | ||
| 127 | } | 36 | } |
| 128 | 37 | ||
| 129 | std::chrono::nanoseconds NativeClock::GetTimeNS() { | 38 | u64 NativeClock::GetHostTicksNow() const { |
| 130 | const u64 rtsc_value = GetRTSC(); | 39 | return FencedRDTSC(); |
| 131 | return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)}; | ||
| 132 | } | 40 | } |
| 133 | 41 | ||
| 134 | std::chrono::microseconds NativeClock::GetTimeUS() { | 42 | u64 NativeClock::GetHostTicksElapsed() const { |
| 135 | const u64 rtsc_value = GetRTSC(); | 43 | return FencedRDTSC() - start_ticks; |
| 136 | return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)}; | ||
| 137 | } | 44 | } |
| 138 | 45 | ||
| 139 | std::chrono::milliseconds NativeClock::GetTimeMS() { | 46 | bool NativeClock::IsNative() const { |
| 140 | const u64 rtsc_value = GetRTSC(); | 47 | return true; |
| 141 | return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)}; | ||
| 142 | } | 48 | } |
| 143 | 49 | ||
| 144 | u64 NativeClock::GetClockCycles() { | 50 | } // namespace Common::X64 |
| 145 | const u64 rtsc_value = GetRTSC(); | ||
| 146 | return MultiplyHigh(rtsc_value, clock_rtsc_factor); | ||
| 147 | } | ||
| 148 | |||
| 149 | u64 NativeClock::GetCPUCycles() { | ||
| 150 | const u64 rtsc_value = GetRTSC(); | ||
| 151 | return MultiplyHigh(rtsc_value, cpu_rtsc_factor); | ||
| 152 | } | ||
| 153 | |||
| 154 | void NativeClock::CalculateAndSetFactors() { | ||
| 155 | ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency); | ||
| 156 | us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency); | ||
| 157 | ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency); | ||
| 158 | clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency); | ||
| 159 | cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency); | ||
| 160 | } | ||
| 161 | |||
| 162 | } // namespace X64 | ||
| 163 | |||
| 164 | } // namespace Common | ||
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index 03ca291d8..334415eff 100644 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h | |||
| @@ -3,58 +3,39 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "common/polyfill_thread.h" | ||
| 7 | #include "common/wall_clock.h" | 6 | #include "common/wall_clock.h" |
| 8 | 7 | ||
| 9 | namespace Common { | 8 | namespace Common::X64 { |
| 10 | 9 | ||
| 11 | namespace X64 { | ||
| 12 | class NativeClock final : public WallClock { | 10 | class NativeClock final : public WallClock { |
| 13 | public: | 11 | public: |
| 14 | explicit NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, | 12 | explicit NativeClock(u64 rdtsc_frequency_); |
| 15 | u64 rtsc_frequency_); | ||
| 16 | 13 | ||
| 17 | std::chrono::nanoseconds GetTimeNS() override; | 14 | std::chrono::nanoseconds GetTimeNS() const override; |
| 18 | 15 | ||
| 19 | std::chrono::microseconds GetTimeUS() override; | 16 | std::chrono::microseconds GetTimeUS() const override; |
| 20 | 17 | ||
| 21 | std::chrono::milliseconds GetTimeMS() override; | 18 | std::chrono::milliseconds GetTimeMS() const override; |
| 22 | 19 | ||
| 23 | u64 GetClockCycles() override; | 20 | u64 GetCNTPCT() const override; |
| 24 | 21 | ||
| 25 | u64 GetCPUCycles() override; | 22 | u64 GetGPUTick() const override; |
| 26 | 23 | ||
| 27 | void Pause(bool is_paused) override; | 24 | u64 GetHostTicksNow() const override; |
| 28 | 25 | ||
| 29 | private: | 26 | u64 GetHostTicksElapsed() const override; |
| 30 | u64 GetRTSC(); | ||
| 31 | |||
| 32 | void CalculateAndSetFactors(); | ||
| 33 | |||
| 34 | union alignas(16) TimePoint { | ||
| 35 | TimePoint() : pack{} {} | ||
| 36 | u128 pack{}; | ||
| 37 | struct Inner { | ||
| 38 | u64 last_measure{}; | ||
| 39 | u64 accumulated_ticks{}; | ||
| 40 | } inner; | ||
| 41 | }; | ||
| 42 | |||
| 43 | TimePoint time_point; | ||
| 44 | 27 | ||
| 45 | // factors | 28 | bool IsNative() const override; |
| 46 | u64 clock_rtsc_factor{}; | ||
| 47 | u64 cpu_rtsc_factor{}; | ||
| 48 | u64 ns_rtsc_factor{}; | ||
| 49 | u64 us_rtsc_factor{}; | ||
| 50 | u64 ms_rtsc_factor{}; | ||
| 51 | 29 | ||
| 52 | u64 rtsc_frequency; | 30 | private: |
| 53 | 31 | u64 start_ticks; | |
| 54 | std::jthread time_sync_thread; | 32 | u64 rdtsc_frequency; |
| 33 | |||
| 34 | u64 ns_rdtsc_factor; | ||
| 35 | u64 us_rdtsc_factor; | ||
| 36 | u64 ms_rdtsc_factor; | ||
| 37 | u64 cntpct_rdtsc_factor; | ||
| 38 | u64 gputick_rdtsc_factor; | ||
| 55 | }; | 39 | }; |
| 56 | } // namespace X64 | ||
| 57 | |||
| 58 | u64 EstimateRDTSCFrequency(); | ||
| 59 | 40 | ||
| 60 | } // namespace Common | 41 | } // namespace Common::X64 |
diff --git a/src/common/x64/rdtsc.cpp b/src/common/x64/rdtsc.cpp new file mode 100644 index 000000000..9273274a3 --- /dev/null +++ b/src/common/x64/rdtsc.cpp | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <thread> | ||
| 5 | |||
| 6 | #include "common/steady_clock.h" | ||
| 7 | #include "common/uint128.h" | ||
| 8 | #include "common/x64/rdtsc.h" | ||
| 9 | |||
| 10 | namespace Common::X64 { | ||
| 11 | |||
| 12 | template <u64 Nearest> | ||
| 13 | static u64 RoundToNearest(u64 value) { | ||
| 14 | const auto mod = value % Nearest; | ||
| 15 | return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod); | ||
| 16 | } | ||
| 17 | |||
| 18 | u64 EstimateRDTSCFrequency() { | ||
| 19 | // Discard the first result measuring the rdtsc. | ||
| 20 | FencedRDTSC(); | ||
| 21 | std::this_thread::sleep_for(std::chrono::milliseconds{1}); | ||
| 22 | FencedRDTSC(); | ||
| 23 | |||
| 24 | // Get the current time. | ||
| 25 | const auto start_time = RealTimeClock::Now(); | ||
| 26 | const u64 tsc_start = FencedRDTSC(); | ||
| 27 | // Wait for 100 milliseconds. | ||
| 28 | std::this_thread::sleep_for(std::chrono::milliseconds{100}); | ||
| 29 | const auto end_time = RealTimeClock::Now(); | ||
| 30 | const u64 tsc_end = FencedRDTSC(); | ||
| 31 | // Calculate differences. | ||
| 32 | const u64 timer_diff = static_cast<u64>( | ||
| 33 | std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); | ||
| 34 | const u64 tsc_diff = tsc_end - tsc_start; | ||
| 35 | const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); | ||
| 36 | return RoundToNearest<100'000>(tsc_freq); | ||
| 37 | } | ||
| 38 | |||
| 39 | } // namespace Common::X64 | ||
diff --git a/src/common/x64/rdtsc.h b/src/common/x64/rdtsc.h new file mode 100644 index 000000000..0ec4f52f9 --- /dev/null +++ b/src/common/x64/rdtsc.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #ifdef _MSC_VER | ||
| 7 | #include <intrin.h> | ||
| 8 | #endif | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Common::X64 { | ||
| 13 | |||
| 14 | #ifdef _MSC_VER | ||
| 15 | __forceinline static u64 FencedRDTSC() { | ||
| 16 | _mm_lfence(); | ||
| 17 | _ReadWriteBarrier(); | ||
| 18 | const u64 result = __rdtsc(); | ||
| 19 | _mm_lfence(); | ||
| 20 | _ReadWriteBarrier(); | ||
| 21 | return result; | ||
| 22 | } | ||
| 23 | #else | ||
| 24 | static inline u64 FencedRDTSC() { | ||
| 25 | u64 eax; | ||
| 26 | u64 edx; | ||
| 27 | asm volatile("lfence\n\t" | ||
| 28 | "rdtsc\n\t" | ||
| 29 | "lfence\n\t" | ||
| 30 | : "=a"(eax), "=d"(edx)); | ||
| 31 | return (edx << 32) | eax; | ||
| 32 | } | ||
| 33 | #endif | ||
| 34 | |||
| 35 | u64 EstimateRDTSCFrequency(); | ||
| 36 | |||
| 37 | } // namespace Common::X64 | ||