diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-29 05:46:59 -0400 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-29 20:49:47 -0400 |
| commit | 77cad674e5a73cb56ba1ed5d5c97a0d99eb21159 (patch) | |
| tree | c8d27ea10578cc82e718a29049d1f05740c871d9 /src/core/arm | |
| parent | 9c0f7e8ad45200d09052a368a42d4e21a8d99ec4 (diff) | |
core: pause in guest loops that poll the system counter
Title 1's main thread reads CNTPCT ~17 million times a second while it waits for the
GPU (~23% of its samples are the counter read). On an SMT core that spin takes
resources from the sibling thread, often the GPU thread. Two reads within ~1.7 us of
each other now execute two PAUSE instructions first; the guest sees the same values.
EDEN_NX2_CNTPCT_PAUSE=0 turns it off.
Diffstat (limited to 'src/core/arm')
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_64.cpp | 25 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_64.h | 1 |
2 files changed, 25 insertions, 1 deletions
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index c2bb05afba..a2c0124f33 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -5,6 +5,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include <atomic> +#include <cstdlib> +#if defined(_M_X64) || defined(__x86_64__) +#include <immintrin.h> +#endif #include "common/settings.h" #include "core/arm/dynarmic/arm_dynarmic.h" #include "core/arm/dynarmic/arm_dynarmic_64.h" @@ -166,7 +170,26 @@ u64 DynarmicCallbacks64::GetTicksRemaining() { } u64 DynarmicCallbacks64::GetCNTPCT() { - return m_parent.m_system.CoreTiming().GetClockTicks(); + const u64 ticks = m_parent.m_system.CoreTiming().GetClockTicks(); + // Guest code that waits by polling the counter (title 1's main thread reads it ~17 million + // times a second while waiting for the GPU) keeps a host core busy at full speed, and the + // GPU thread is often on the other half of that SMT core. Two reads within ~1.7 us of each + // other are such a loop: pause, which hands the core's resources to the sibling thread for a + // moment. The guest sees the same counter. EDEN_NX2_CNTPCT_PAUSE=0 turns it off. + static const bool pause_spins = [] { + const char* value = std::getenv("EDEN_NX2_CNTPCT_PAUSE"); + return value == nullptr || value[0] != '0'; + }(); + if (pause_spins && ticks - m_last_cntpct < 32) { +#if defined(_M_X64) || defined(__x86_64__) + _mm_pause(); + _mm_pause(); +#elif defined(__aarch64__) + asm volatile("yield"); +#endif + } + m_last_cntpct = ticks; + return ticks; } bool DynarmicCallbacks64::CheckMemoryAccess(u64 addr, u64 size, Kernel::DebugWatchpointType type) { diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 5ea5dde8a6..acd1fe773f 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -65,6 +65,7 @@ public: Core::Memory::Memory& m_memory; u64 m_tpidrro_el0{}; u64 m_tpidr_el0{}; + u64 m_last_cntpct{}; ///< The previous counter read (GetCNTPCT spin detection) Kernel::KProcess* m_process{}; const bool m_debugger_enabled{}; const bool m_check_memory_access{}; |
