Common: Refactor & Document Wall clock.

This commit is contained in:
Fernando Sahmkow 2020-02-10 11:20:40 -04:00
parent 234b5ff6a9
commit e3524d1142
6 changed files with 50 additions and 51 deletions

View File

@ -6,12 +6,34 @@
#include <intrin.h> #include <intrin.h>
#pragma intrinsic(_umul128) #pragma intrinsic(_umul128)
#pragma intrinsic(_udiv128)
#endif #endif
#include <cstring> #include <cstring>
#include "common/uint128.h" #include "common/uint128.h"
namespace Common { namespace Common {
#ifdef _MSC_VER
u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
u128 r{};
r[0] = _umul128(a, b, &r[1]);
u64 remainder;
return _udiv128(r[1], r[0], d, &remainder);
}
#else
u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
const u64 diva = a / d;
const u64 moda = a % d;
const u64 divb = b / d;
const u64 modb = b % d;
return diva * b + moda * divb + moda * modb / d;
}
#endif
u128 Multiply64Into128(u64 a, u64 b) { u128 Multiply64Into128(u64 a, u64 b) {
u128 result; u128 result;
#ifdef _MSC_VER #ifdef _MSC_VER

View File

@ -9,6 +9,9 @@
namespace Common { namespace Common {
// This function multiplies 2 u64 values and divides it by a u64 value.
u64 MultiplyAndDivide64(u64 a, u64 b, u64 d);
// This function multiplies 2 u64 values and produces a u128 value; // This function multiplies 2 u64 values and produces a u128 value;
u128 Multiply64Into128(u64 a, u64 b); u128 Multiply64Into128(u64 a, u64 b);

View File

@ -58,7 +58,7 @@ private:
#ifdef ARCHITECTURE_x86_64 #ifdef ARCHITECTURE_x86_64
WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) {
const auto& caps = GetCPUCaps(); const auto& caps = GetCPUCaps();
u64 rtsc_frequency = 0; u64 rtsc_frequency = 0;
if (caps.invariant_tsc) { if (caps.invariant_tsc) {
@ -70,19 +70,16 @@ WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_cloc
} }
} }
if (rtsc_frequency == 0) { if (rtsc_frequency == 0) {
return static_cast<WallClock*>( return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
new StandardWallClock(emulated_cpu_frequency, emulated_clock_frequency));
} else { } else {
return static_cast<WallClock*>( return std::make_unique<X64::NativeClock>(emulated_cpu_frequency, emulated_clock_frequency, rtsc_frequency);
new X64::NativeClock(emulated_cpu_frequency, emulated_clock_frequency, rtsc_frequency));
} }
} }
#else #else
WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) {
return static_cast<WallClock*>( return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
new StandardWallClock(emulated_cpu_frequency, emulated_clock_frequency));
} }
#endif #endif

View File

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <chrono> #include <chrono>
#include <memory>
#include "common/common_types.h" #include "common/common_types.h"
@ -12,10 +13,20 @@ namespace Common {
class WallClock { class WallClock {
public: public:
/// Returns current wall time in nanoseconds
virtual std::chrono::nanoseconds GetTimeNS() = 0; virtual std::chrono::nanoseconds GetTimeNS() = 0;
/// Returns current wall time in microseconds
virtual std::chrono::microseconds GetTimeUS() = 0; virtual std::chrono::microseconds GetTimeUS() = 0;
/// Returns current wall time in milliseconds
virtual std::chrono::milliseconds GetTimeMS() = 0; virtual std::chrono::milliseconds GetTimeMS() = 0;
/// Returns current wall time in emulated clock cycles
virtual u64 GetClockCycles() = 0; virtual u64 GetClockCycles() = 0;
/// Returns current wall time in emulated cpu cycles
virtual u64 GetCPUCycles() = 0; virtual u64 GetCPUCycles() = 0;
/// Tells if the wall clock, uses the host CPU's hardware clock /// Tells if the wall clock, uses the host CPU's hardware clock
@ -35,6 +46,6 @@ private:
bool is_native; bool is_native;
}; };
WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency); std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency);
} // namespace Common } // namespace Common

View File

@ -11,44 +11,11 @@
#include <x86intrin.h> #include <x86intrin.h>
#endif #endif
#include "common/uint128.h"
#include "common/x64/native_clock.h" #include "common/x64/native_clock.h"
namespace Common { namespace Common {
#ifdef _MSC_VER
namespace {
struct uint128 {
u64 low;
u64 high;
};
u64 umuldiv64(u64 a, u64 b, u64 d) {
uint128 r{};
r.low = _umul128(a, b, &r.high);
u64 remainder;
return _udiv128(r.high, r.low, d, &remainder);
}
} // namespace
#else
namespace {
u64 umuldiv64(u64 a, u64 b, u64 d) {
const u64 diva = a / d;
const u64 moda = a % d;
const u64 divb = b / d;
const u64 modb = b % d;
return diva * b + moda * divb + moda * modb / d;
}
} // namespace
#endif
u64 EstimateRDTSCFrequency() { u64 EstimateRDTSCFrequency() {
const auto milli_10 = std::chrono::milliseconds{10}; const auto milli_10 = std::chrono::milliseconds{10};
// get current time // get current time
@ -70,7 +37,7 @@ u64 EstimateRDTSCFrequency() {
const u64 timer_diff = const u64 timer_diff =
std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count(); std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count();
const u64 tsc_diff = tscEnd - tscStart; const u64 tsc_diff = tscEnd - tscStart;
const u64 tsc_freq = umuldiv64(tsc_diff, 1000000000ULL, timer_diff); const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
return tsc_freq; return tsc_freq;
} }
@ -100,27 +67,27 @@ u64 NativeClock::GetRTSC() {
std::chrono::nanoseconds NativeClock::GetTimeNS() { std::chrono::nanoseconds NativeClock::GetTimeNS() {
const u64 rtsc_value = GetRTSC(); const u64 rtsc_value = GetRTSC();
return std::chrono::nanoseconds{umuldiv64(rtsc_value, 1000000000, rtsc_frequency)}; return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)};
} }
std::chrono::microseconds NativeClock::GetTimeUS() { std::chrono::microseconds NativeClock::GetTimeUS() {
const u64 rtsc_value = GetRTSC(); const u64 rtsc_value = GetRTSC();
return std::chrono::microseconds{umuldiv64(rtsc_value, 1000000, rtsc_frequency)}; return std::chrono::microseconds{MultiplyAndDivide64(rtsc_value, 1000000, rtsc_frequency)};
} }
std::chrono::milliseconds NativeClock::GetTimeMS() { std::chrono::milliseconds NativeClock::GetTimeMS() {
const u64 rtsc_value = GetRTSC(); const u64 rtsc_value = GetRTSC();
return std::chrono::milliseconds{umuldiv64(rtsc_value, 1000, rtsc_frequency)}; return std::chrono::milliseconds{MultiplyAndDivide64(rtsc_value, 1000, rtsc_frequency)};
} }
u64 NativeClock::GetClockCycles() { u64 NativeClock::GetClockCycles() {
const u64 rtsc_value = GetRTSC(); const u64 rtsc_value = GetRTSC();
return umuldiv64(rtsc_value, emulated_clock_frequency, rtsc_frequency); return MultiplyAndDivide64(rtsc_value, emulated_clock_frequency, rtsc_frequency);
} }
u64 NativeClock::GetCPUCycles() { u64 NativeClock::GetCPUCycles() {
const u64 rtsc_value = GetRTSC(); const u64 rtsc_value = GetRTSC();
return umuldiv64(rtsc_value, emulated_cpu_frequency, rtsc_frequency); return MultiplyAndDivide64(rtsc_value, emulated_cpu_frequency, rtsc_frequency);
} }
} // namespace X64 } // namespace X64

View File

@ -36,8 +36,7 @@ struct CoreTiming::Event {
}; };
CoreTiming::CoreTiming() { CoreTiming::CoreTiming() {
Common::WallClock* wall = Common::CreateBestMatchingClock(Core::Timing::BASE_CLOCK_RATE, Core::Timing::CNTFREQ); clock = Common::CreateBestMatchingClock(Core::Timing::BASE_CLOCK_RATE, Core::Timing::CNTFREQ);
clock = std::unique_ptr<Common::WallClock>(wall);
} }
CoreTiming::~CoreTiming() = default; CoreTiming::~CoreTiming() = default;