VMManager: Set affinities for threads

This commit is contained in:
Connor McLaughlin 2021-10-31 19:59:31 +10:00 committed by refractionpcsx2
parent 9b7ae498d2
commit 543fb282fe
10 changed files with 308 additions and 10 deletions

View File

@ -268,6 +268,8 @@ void QtHost::SetDefaultConfig()
{
EmuConfig = Pcsx2Config();
EmuFolders::SetDefaults();
EmuFolders::EnsureFoldersExist();
VMManager::SetHardwareDependentDefaultSettings(EmuConfig);
SettingsInterface& si = *s_base_settings_interface.get();
si.SetUIntValue("UI", "SettingsVersion", SETTINGS_VERSION);

View File

@ -40,6 +40,7 @@ SystemSettingsWidget::SystemSettingsWidget(SettingsDialog* dialog, QWidget* pare
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.eeCycleSkipping, "EmuCore/Speedhacks", "EECycleSkip", DEFAULT_EE_CYCLE_SKIP);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.affinityControl, "EmuCore/CPU", "AffinityControlMode", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.MTVU, "EmuCore/Speedhacks", "vuThread", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.instantVU1, "EmuCore/Speedhacks", "vu1Instant", true);

View File

@ -171,6 +171,52 @@
</item>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Affinity Control:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="affinityControl">
<item>
<property name="text">
<string>Disabled</string>
</property>
</item>
<item>
<property name="text">
<string>EE &gt; VU &gt; GS</string>
</property>
</item>
<item>
<property name="text">
<string>EE &gt; GS &gt; VU</string>
</property>
</item>
<item>
<property name="text">
<string>VU &gt; EE &gt; GS</string>
</property>
</item>
<item>
<property name="text">
<string>VU &gt; GS &gt; EE</string>
</property>
</item>
<item>
<property name="text">
<string>GS &gt; EE &gt; VU</string>
</property>
</item>
<item>
<property name="text">
<string>GS &gt; VU &gt; EE</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -1604,6 +1604,7 @@ target_link_libraries(PCSX2_FLAGS INTERFACE
ryml
chdr-static
libzip::zip
cpuinfo
ZLIB::ZLIB
PkgConfig::SOUNDTOUCH
PkgConfig::SAMPLERATE

View File

@ -399,6 +399,8 @@ struct Pcsx2Config
SSE_MXCSR sseMXCSR;
SSE_MXCSR sseVUMXCSR;
u32 AffinityControlMode;
CpuOptions();
void LoadSave(SettingsWrapper& wrap);
void ApplySanityCheck();
@ -407,7 +409,7 @@ struct Pcsx2Config
bool operator==(const CpuOptions& right) const
{
return OpEqu(sseMXCSR) && OpEqu(sseVUMXCSR) && OpEqu(Recompiler);
return OpEqu(sseMXCSR) && OpEqu(sseVUMXCSR) && OpEqu(AffinityControlMode) && OpEqu(Recompiler);
}
bool operator!=(const CpuOptions& right) const

View File

@ -21,6 +21,10 @@
#include "PerformanceMetrics.h"
#include "common/StringUtil.h"
#ifdef PCSX2_CORE
#include "VMManager.h"
#endif
#define ENABLE_DRAW_STATS 0
int GSRasterizerData::s_counter = 0;
@ -1176,7 +1180,25 @@ GSRasterizerList::~GSRasterizerList()
void GSRasterizerList::OnWorkerStartup(int i)
{
Threading::SetNameOfCurrentThread(StringUtil::StdStringFromFormat("GS-SW-%d", i).c_str());
PerformanceMetrics::SetGSSWThread(i, Threading::ThreadHandle::GetForCallingThread());
Threading::ThreadHandle handle(Threading::ThreadHandle::GetForCallingThread());
#ifdef PCSX2_CORE
if (EmuConfig.Cpu.AffinityControlMode != 0)
{
const std::vector<u32>& procs = VMManager::GetSortedProcessorList();
const u32 processor_index = (THREAD_VU1 ? 3 : 2) + i;
if (processor_index < procs.size())
{
const u32 procid = procs[processor_index];
const u64 affinity = static_cast<u64>(1) << procid;
Console.WriteLn("Pinning GS thread %d to CPU %u (0x%llx)", i, procid, affinity);
handle.SetAffinity(affinity);
}
}
#endif
PerformanceMetrics::SetGSSWThread(i, std::move(handle));
}
void GSRasterizerList::OnWorkerShutdown(int i)

View File

@ -238,12 +238,14 @@ Pcsx2Config::CpuOptions::CpuOptions()
{
sseMXCSR.bitmask = DEFAULT_sseMXCSR;
sseVUMXCSR.bitmask = DEFAULT_sseVUMXCSR;
AffinityControlMode = 0;
}
void Pcsx2Config::CpuOptions::ApplySanityCheck()
{
sseMXCSR.ClearExceptionFlags().DisableExceptions();
sseVUMXCSR.ClearExceptionFlags().DisableExceptions();
AffinityControlMode = std::min<u32>(AffinityControlMode, 6);
Recompiler.ApplySanityCheck();
}
@ -255,6 +257,7 @@ void Pcsx2Config::CpuOptions::LoadSave(SettingsWrapper& wrap)
SettingsWrapBitBoolEx(sseMXCSR.DenormalsAreZero, "FPU.DenormalsAreZero");
SettingsWrapBitBoolEx(sseMXCSR.FlushToZero, "FPU.FlushToZero");
SettingsWrapBitfieldEx(sseMXCSR.RoundingControl, "FPU.Roundmode");
SettingsWrapEntry(AffinityControlMode);
SettingsWrapBitBoolEx(sseVUMXCSR.DenormalsAreZero, "VU.DenormalsAreZero");
SettingsWrapBitBoolEx(sseVUMXCSR.FlushToZero, "VU.FlushToZero");

View File

@ -18,6 +18,7 @@
#include "VMManager.h"
#include <atomic>
#include <sstream>
#include <mutex>
#include "common/Console.h"
@ -26,6 +27,7 @@
#include "common/StringUtil.h"
#include "common/SettingsWrapper.h"
#include "common/Timer.h"
#include "common/Threading.h"
#include "fmt/core.h"
#include "Counters.h"
@ -98,7 +100,8 @@ namespace VMManager
std::string filename, s32 slot_for_message);
static void SetTimerResolutionIncreased(bool enabled);
static void SetEmuThreadAffinities(bool force);
static void EnsureCPUInfoInitialized();
static void SetEmuThreadAffinities();
} // namespace VMManager
static std::unique_ptr<SysMainMemory> s_vm_memory;
@ -924,7 +927,7 @@ bool VMManager::Initialize(const VMBootParameters& boot_params)
UpdateRunningGame(true, false);
SetEmuThreadAffinities(true);
SetEmuThreadAffinities();
PerformanceMetrics::Clear();
@ -1410,6 +1413,12 @@ void VMManager::CheckForCPUConfigChanges(const Pcsx2Config& old_config)
// possible and reset next time we're called.
s_cpu_implementation_changed = true;
}
if (EmuConfig.Cpu.AffinityControlMode != old_config.Cpu.AffinityControlMode ||
EmuConfig.Speedhacks.vuThread != old_config.Speedhacks.vuThread)
{
SetEmuThreadAffinities();
}
}
void VMManager::CheckForGSConfigChanges(const Pcsx2Config& old_config)
@ -1573,10 +1582,7 @@ void VMManager::ApplySettings()
LoadSettings();
if (HasValidVM())
{
CheckForConfigChanges(old_config);
SetEmuThreadAffinities(false);
}
}
bool VMManager::ReloadGameSettings()
@ -1722,7 +1728,211 @@ void VMManager::SetTimerResolutionIncreased(bool enabled)
#endif
void VMManager::SetEmuThreadAffinities(bool force)
static std::vector<u32> s_processor_list;
static std::once_flag s_processor_list_initialized;
#if defined(__linux__) || defined(_WIN32)
#include "cpuinfo.h"
static u32 GetProcessorIdForProcessor(const cpuinfo_processor* proc)
{
Console.Error("(SetEmuThreadAffinities) Not implemented");
#if defined(__linux__)
return static_cast<u32>(proc->linux_id);
#elif defined(_WIN32)
return static_cast<u32>(proc->windows_processor_id);
#else
return 0;
#endif
}
static void InitializeCPUInfo()
{
if (!cpuinfo_initialize())
{
Console.Error("Failed to initialize cpuinfo");
return;
}
const u32 cluster_count = cpuinfo_get_clusters_count();
if (cluster_count == 0)
{
Console.Error("Invalid CPU count returned");
return;
}
Console.WriteLn(Color_StrongYellow, "Processor count: %u cores, %u processors", cpuinfo_get_cores_count(), cpuinfo_get_processors_count());
Console.WriteLn(Color_StrongYellow, "Cluster count: %u", cluster_count);
static std::vector<const cpuinfo_processor*> ordered_processors;
for (u32 i = 0; i < cluster_count; i++)
{
const cpuinfo_cluster* cluster = cpuinfo_get_cluster(i);
for (u32 j = 0; j < cluster->processor_count; j++)
{
const cpuinfo_processor* proc = cpuinfo_get_processor(cluster->processor_start + j);
if (!proc)
continue;
ordered_processors.push_back(proc);
}
}
// find the large and small clusters based on frequency
// this is assuming the large cluster is always clocked higher
// sort based on core, so that hyperthreads get pushed down
std::sort(ordered_processors.begin(), ordered_processors.end(), [](const cpuinfo_processor* lhs, const cpuinfo_processor* rhs) {
return (lhs->core->frequency > rhs->core->frequency || lhs->smt_id < rhs->smt_id);
});
s_processor_list.reserve(ordered_processors.size());
std::stringstream ss;
ss << "Ordered processor list: ";
for (const cpuinfo_processor* proc : ordered_processors)
{
if (proc != ordered_processors.front())
ss << ", ";
const u32 procid = GetProcessorIdForProcessor(proc);
ss << procid;
if (proc->smt_id != 0)
ss << "[SMT " << proc->smt_id << "]";
s_processor_list.push_back(procid);
}
Console.WriteLn(ss.str());
}
static void SetMTVUAndAffinityControlDefault(Pcsx2Config& config)
{
VMManager::EnsureCPUInfoInitialized();
const u32 cluster_count = cpuinfo_get_clusters_count();
if (cluster_count == 0)
{
Console.Error("Invalid CPU count returned");
return;
}
Console.WriteLn("Cluster count: %u", cluster_count);
for (u32 i = 0; i < cluster_count; i++)
{
const cpuinfo_cluster* cluster = cpuinfo_get_cluster(i);
Console.WriteLn(" Cluster %u: %u cores and %u processors at %u MHz",
i, cluster->core_count, cluster->processor_count, static_cast<u32>(cluster->frequency /* / 1000000u*/));
}
const bool has_big_little = cluster_count > 1;
Console.WriteLn("Big-Little: %s", has_big_little ? "yes" : "no");
const u32 big_cores = cpuinfo_get_cluster(0)->core_count + ((cluster_count > 2) ? cpuinfo_get_cluster(1)->core_count : 0u);
Console.WriteLn("Guessing we have %u big/medium cores...", big_cores);
bool mtvu_enable;
bool affinity_control;
if (big_cores >= 3 || big_cores == 1)
{
Console.WriteLn(" So enabling MTVU and disabling affinity control");
mtvu_enable = true;
affinity_control = false;
}
else
{
Console.WriteLn(" So disabling MTVU and enabling affinity control");
mtvu_enable = false;
affinity_control = true;
}
config.Speedhacks.vuThread = mtvu_enable;
config.Cpu.AffinityControlMode = affinity_control ? 1 : 0;
}
#else
static void InitializeCPUInfo()
{
DevCon.WriteLn("(VMManager) InitializeCPUInfo() not implemented.");
}
static void SetMTVUAndAffinityControlDefault(Pcsx2Config& config)
{
}
#endif
void VMManager::EnsureCPUInfoInitialized()
{
std::call_once(s_processor_list_initialized, InitializeCPUInfo);
}
void VMManager::SetEmuThreadAffinities()
{
EnsureCPUInfoInitialized();
if (s_processor_list.empty())
{
// not supported on this platform
return;
}
if (EmuConfig.Cpu.AffinityControlMode == 0 ||
s_processor_list.size() < (EmuConfig.Speedhacks.vuThread ? 3 : 2))
{
if (EmuConfig.Cpu.AffinityControlMode != 0)
Console.Error("Insufficient processors for affinity control.");
GetMTGS().GetThreadHandle().SetAffinity(0);
vu1Thread.GetThreadHandle().SetAffinity(0);
s_vm_thread_handle.SetAffinity(0);
return;
}
static constexpr u8 processor_assignment[7][2][3] = {
//EE xx GS EE VU GS
{{0, 2, 1}, {0, 1, 2}}, // Disabled
{{0, 2, 1}, {0, 1, 2}}, // EE > VU > GS
{{0, 2, 1}, {0, 2, 1}}, // EE > GS > VU
{{0, 2, 1}, {1, 0, 2}}, // VU > EE > GS
{{1, 2, 0}, {2, 0, 1}}, // VU > GS > EE
{{1, 2, 0}, {1, 2, 0}}, // GS > EE > VU
{{1, 2, 0}, {2, 1, 0}}, // GS > VU > EE
};
// steal vu's thread if mtvu is off
const u8* this_proc_assigment = processor_assignment[EmuConfig.Cpu.AffinityControlMode][EmuConfig.Speedhacks.vuThread];
const u32 ee_index = s_processor_list[this_proc_assigment[0]];
const u32 vu_index = s_processor_list[this_proc_assigment[1]];
const u32 gs_index = s_processor_list[this_proc_assigment[2]];
Console.WriteLn("Processor order assignment: EE=%u, VU=%u, GS=%u",
this_proc_assigment[0], this_proc_assigment[1], this_proc_assigment[2]);
const u64 ee_affinity = static_cast<u64>(1) << ee_index;
Console.WriteLn(Color_StrongGreen, "EE thread is on processor %u (0x%llx)", ee_index, ee_affinity);
s_vm_thread_handle.SetAffinity(ee_affinity);
if (EmuConfig.Speedhacks.vuThread)
{
const u64 vu_affinity = static_cast<u64>(1) << vu_index;
Console.WriteLn(Color_StrongGreen, "VU thread is on processor %u (0x%llx)", vu_index, vu_affinity);
vu1Thread.GetThreadHandle().SetAffinity(vu_affinity);
}
else
{
vu1Thread.GetThreadHandle().SetAffinity(0);
}
const u64 gs_affinity = static_cast<u64>(1) << gs_index;
Console.WriteLn(Color_StrongGreen, "GS thread is on processor %u (0x%llx)", gs_index, gs_affinity);
GetMTGS().GetThreadHandle().SetAffinity(gs_affinity);
}
void VMManager::SetHardwareDependentDefaultSettings(Pcsx2Config& config)
{
SetMTVUAndAffinityControlDefault(config);
}
const std::vector<u32>& VMManager::GetSortedProcessorList()
{
EnsureCPUInfoInitialized();
return s_processor_list;
}

View File

@ -159,6 +159,13 @@ namespace VMManager
/// If the scale is set to 0, the internal resolution will be used, otherwise it is treated as a multiplier to 1x.
void RequestDisplaySize(float scale = 0.0f);
/// Sets default settings based on hardware configuration.
void SetHardwareDependentDefaultSettings(Pcsx2Config& config);
/// Returns a list of processors in the system, and their corresponding affinity mask.
/// This list is ordered by most performant to least performant for pinning threads to.
const std::vector<u32>& GetSortedProcessorList();
/// Internal callbacks, implemented in the emu core.
namespace Internal
{

View File

@ -48,6 +48,7 @@
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\libzip;$(SolutionDir)3rdparty\libzip\libzip\lib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\d3d12memalloc\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\zstd\zstd\lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\cpuinfo\include</AdditionalIncludeDirectories>
<ExceptionHandling>Async</ExceptionHandling>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
@ -791,6 +792,9 @@
<ProjectReference Include="..\3rdparty\libzip\libzip.vcxproj">
<Project>{20b2e9fe-f020-42a0-b324-956f5b06ea68}</Project>
</ProjectReference>
<ProjectReference Include="..\3rdparty\cpuinfo\cpuinfo.vcxproj">
<Project>{7e183337-a7e9-460c-9d3d-568bc9f9bcc1}</Project>
</ProjectReference>
<ProjectReference Include="..\3rdparty\sdl2\SDL.vcxproj">
<Project>{812b4434-fd6b-4cb2-8865-5fd8eb34b046}</Project>
</ProjectReference>
@ -803,4 +807,4 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>