Common: Refactor & Document Wall clock.
This commit is contained in:
parent
234b5ff6a9
commit
e3524d1142
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Reference in New Issue