Common: Refactor & Document Wall clock.
This commit is contained in:
parent
234b5ff6a9
commit
e3524d1142
|
@ -6,12 +6,34 @@
|
|||
#include <intrin.h>
|
||||
|
||||
#pragma intrinsic(_umul128)
|
||||
#pragma intrinsic(_udiv128)
|
||||
#endif
|
||||
#include <cstring>
|
||||
#include "common/uint128.h"
|
||||
|
||||
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 result;
|
||||
#ifdef _MSC_VER
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
|
||||
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;
|
||||
u128 Multiply64Into128(u64 a, u64 b);
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ private:
|
|||
|
||||
#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();
|
||||
u64 rtsc_frequency = 0;
|
||||
if (caps.invariant_tsc) {
|
||||
|
@ -70,19 +70,16 @@ WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_cloc
|
|||
}
|
||||
}
|
||||
if (rtsc_frequency == 0) {
|
||||
return static_cast<WallClock*>(
|
||||
new StandardWallClock(emulated_cpu_frequency, emulated_clock_frequency));
|
||||
return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
|
||||
} else {
|
||||
return static_cast<WallClock*>(
|
||||
new X64::NativeClock(emulated_cpu_frequency, emulated_clock_frequency, rtsc_frequency));
|
||||
return std::make_unique<X64::NativeClock>(emulated_cpu_frequency, emulated_clock_frequency, rtsc_frequency);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) {
|
||||
return static_cast<WallClock*>(
|
||||
new StandardWallClock(emulated_cpu_frequency, emulated_clock_frequency));
|
||||
std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) {
|
||||
return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
|
@ -12,10 +13,20 @@ namespace Common {
|
|||
|
||||
class WallClock {
|
||||
public:
|
||||
|
||||
/// Returns current wall time in nanoseconds
|
||||
virtual std::chrono::nanoseconds GetTimeNS() = 0;
|
||||
|
||||
/// Returns current wall time in microseconds
|
||||
virtual std::chrono::microseconds GetTimeUS() = 0;
|
||||
|
||||
/// Returns current wall time in milliseconds
|
||||
virtual std::chrono::milliseconds GetTimeMS() = 0;
|
||||
|
||||
/// Returns current wall time in emulated clock cycles
|
||||
virtual u64 GetClockCycles() = 0;
|
||||
|
||||
/// Returns current wall time in emulated cpu cycles
|
||||
virtual u64 GetCPUCycles() = 0;
|
||||
|
||||
/// Tells if the wall clock, uses the host CPU's hardware clock
|
||||
|
@ -35,6 +46,6 @@ private:
|
|||
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
|
||||
|
|
|
@ -11,44 +11,11 @@
|
|||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
#include "common/uint128.h"
|
||||
#include "common/x64/native_clock.h"
|
||||
|
||||
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() {
|
||||
const auto milli_10 = std::chrono::milliseconds{10};
|
||||
// get current time
|
||||
|
@ -70,7 +37,7 @@ u64 EstimateRDTSCFrequency() {
|
|||
const u64 timer_diff =
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count();
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -100,27 +67,27 @@ u64 NativeClock::GetRTSC() {
|
|||
|
||||
std::chrono::nanoseconds NativeClock::GetTimeNS() {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
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
|
||||
|
|
|
@ -36,8 +36,7 @@ struct CoreTiming::Event {
|
|||
};
|
||||
|
||||
CoreTiming::CoreTiming() {
|
||||
Common::WallClock* wall = Common::CreateBestMatchingClock(Core::Timing::BASE_CLOCK_RATE, Core::Timing::CNTFREQ);
|
||||
clock = std::unique_ptr<Common::WallClock>(wall);
|
||||
clock = Common::CreateBestMatchingClock(Core::Timing::BASE_CLOCK_RATE, Core::Timing::CNTFREQ);
|
||||
}
|
||||
|
||||
CoreTiming::~CoreTiming() = default;
|
||||
|
|
Reference in New Issue