diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index d50239e0e2..8d9c24dafc 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "qemu/thread.h" #include "sysemu/tcg.h" #include "sysemu/replay.h" #include "sysemu/cpu-timers.h" @@ -62,11 +63,61 @@ static void mttcg_force_rcu(Notifier *notify, void *data) * current CPUState for a given thread. */ +static void qemu_thread_set_affinity_simple(int core_id) { + int nbits = +#ifdef _WIN32 + 64 +#else + sysconf(_SC_NPROCESSORS_ONLN); +#endif + unsigned long *bitmap = bitmap_new(nbits); + + core_id = core_id % nbits; + set_bit(core_id, bitmap); + + QemuThread thread; + qemu_thread_get_self(&thread); + + int ret = qemu_thread_set_affinity(&thread, bitmap, nbits); + if (ret) { + fprintf(stderr, "Setting CPU affinity failed\n"); + } + g_free(bitmap); +} + +static void qemu_thread_clear_affinity(void) { + int nbits = +#ifdef _WIN32 + 64 +#else + sysconf(_SC_NPROCESSORS_ONLN); +#endif + unsigned long *bitmap = bitmap_new(nbits); + + for (int i = 0; i < nbits; i++) { + set_bit(i, bitmap); + } + + QemuThread thread; + qemu_thread_get_self(&thread); + + int ret = qemu_thread_set_affinity(&thread, bitmap, nbits); + if (ret) { + fprintf(stderr, "Setting CPU affinity failed\n"); + } + g_free(bitmap); +} + +#include "ui/xemu-settings.h" +static bool pinned = false; + static void *mttcg_cpu_thread_fn(void *arg) { MttcgForceRcuNotifier force_rcu; CPUState *cpu = arg; + qemu_thread_set_affinity_simple(0); + assert(tcg_enabled()); g_assert(!icount_enabled()); @@ -89,6 +140,15 @@ static void *mttcg_cpu_thread_fn(void *arg) cpu->exit_request = 1; do { + if (pinned != g_config.perf.pin_cpu_thread) { + if (g_config.perf.pin_cpu_thread) { + qemu_thread_set_affinity_simple(0); + } else { + qemu_thread_clear_affinity(); + } + pinned = g_config.perf.pin_cpu_thread; + } + if (cpu_can_run(cpu)) { int r; qemu_mutex_unlock_iothread(); diff --git a/config_spec.yml b/config_spec.yml index 5d894a6f86..ffdc24b536 100644 --- a/config_spec.yml +++ b/config_spec.yml @@ -209,3 +209,6 @@ perf: cache_shaders: type: bool default: true + pin_cpu_thread: + type: bool + default: false diff --git a/ui/xui/main-menu.cc b/ui/xui/main-menu.cc index 50db1aae43..4aa6bdce12 100644 --- a/ui/xui/main-menu.cc +++ b/ui/xui/main-menu.cc @@ -58,6 +58,8 @@ void MainMenuGeneralView::Draw() Toggle("Hard FPU emulation", &g_config.perf.hard_fpu, "Use hardware-accelerated floating point emulation (requires restart)"); #endif + Toggle("Set CPU thread affinity", &g_config.perf.pin_cpu_thread, + "Set CPU thread affinity to Logical CPU 0"); Toggle("Cache shaders to disk", &g_config.perf.cache_shaders, "Reduce stutter in games by caching previously generated shaders"); diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index 69db254ac7..eb3bd1cd40 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -15,6 +15,7 @@ #include "qemu/thread.h" #include "qemu/notify.h" #include "qemu-thread-common.h" +#include "qemu/bitmap.h" #include static bool name_threads; @@ -480,7 +481,24 @@ void qemu_thread_create(QemuThread *thread, const char *name, int qemu_thread_set_affinity(QemuThread *thread, unsigned long *host_cpus, unsigned long nbits) { - return -ENOSYS; + DWORD_PTR affinity = 0; + DWORD_PTR affinity_prev; + unsigned long value; + assert(nbits <= 64); + + value = find_first_bit(host_cpus, nbits); + while (value < nbits) { + affinity |= 1ULL << value; + value = find_next_bit(host_cpus, nbits, value + 1); + } + + affinity_prev = + SetThreadAffinityMask(qemu_thread_get_handle(thread), affinity); + if (!affinity_prev) { + return -1; + } + + return 0; } int qemu_thread_get_affinity(QemuThread *thread, unsigned long **host_cpus,