Skip to content

Commit 6c35dac

Browse files
authored
Allow autodetection of number of cores for threading (#6824)
* Allow autodetection of threads * cleanup
1 parent 8e64845 commit 6c35dac

2 files changed

Lines changed: 96 additions & 12 deletions

File tree

code/cmdline/cmdline.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2411,10 +2411,6 @@ bool SetCmdlineParams()
24112411

24122412
if (multithreading.found()) {
24132413
Cmdline_multithreading = abs(multithreading.get_int());
2414-
if (Cmdline_multithreading < 1) {
2415-
Cmdline_multithreading = 1;
2416-
Warning(LOCATION,"-threads must be an integer greater or equal to 1. Invalid thread count will be disregarded.");
2417-
}
24182414
}
24192415

24202416
return true;

code/utils/threading.cpp

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,18 @@
99
#include <mutex>
1010
#include <thread>
1111

12+
#ifdef WIN32
13+
#include <windows.h>
14+
#endif
15+
1216
namespace threading {
13-
std::condition_variable wait_for_task;
14-
std::mutex wait_for_task_mutex;
15-
bool wait_for_task_condition;
16-
std::atomic<WorkerThreadTask> worker_task;
17+
static size_t num_threads = 1;
18+
static std::condition_variable wait_for_task;
19+
static std::mutex wait_for_task_mutex;
20+
static bool wait_for_task_condition;
21+
static std::atomic<WorkerThreadTask> worker_task;
1722

18-
SCP_vector<std::thread> worker_threads;
23+
static SCP_vector<std::thread> worker_threads;
1924

2025
//Internal Functions
2126
static void mp_worker_thread_main(size_t threadIdx) {
@@ -37,6 +42,82 @@ namespace threading {
3742
}
3843
}
3944

45+
static size_t get_number_of_physical_cores_fallback() {
46+
unsigned int hardware_threads = std::thread::hardware_concurrency();
47+
if (hardware_threads > 0) {
48+
return hardware_threads;
49+
}
50+
else {
51+
Warning(LOCATION, "Could not autodetect available number of threads! Disabling multithreading...");
52+
return 1;
53+
}
54+
}
55+
56+
//We don't want to rely on std::thread::hardware_concurrency() unless we have to, as it reports threads, not physical cores, and FSO doesn't gain much from hyperthreaded threads at the moment.
57+
#ifdef WIN32
58+
static size_t get_number_of_physical_cores() {
59+
auto glpi = (BOOL (WINAPI *)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD)) GetProcAddress(
60+
GetModuleHandle(TEXT("kernel32")),
61+
"GetLogicalProcessorInformation");
62+
63+
if (glpi == nullptr)
64+
return get_number_of_physical_cores_fallback();
65+
66+
DWORD length = 0;
67+
glpi(nullptr, &length);
68+
SCP_vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> infoBuffer(length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
69+
DWORD error = glpi(infoBuffer.data(), &length);
70+
71+
if (error != 0)
72+
return get_number_of_physical_cores_fallback();
73+
74+
size_t num_cores = 0;
75+
for (const auto& info : infoBuffer) {
76+
if (info.Relationship == RelationProcessorCore && info.ProcessorMask != 0)
77+
num_cores++;
78+
}
79+
80+
if (num_cores < 1) {
81+
//invalid results, try fallback
82+
return get_number_of_physical_cores_fallback();
83+
}
84+
else {
85+
return num_cores;
86+
}
87+
}
88+
#elif defined SCP_UNIX
89+
static size_t get_number_of_physical_cores() {
90+
try {
91+
std::ifstream cpuinfo("/proc/cpuinfo");
92+
SCP_string line;
93+
while (std::getline(cpuinfo, line)) {
94+
//Looking for a cpu cores property is fine assuming a user has only one physical CPU socket. If they have multiple CPU's, this'll underreport the core count, but that should be very rare in typical configurations
95+
if (line.find("cpu cores") != SCP_string::npos){
96+
size_t numberpos = line.find(": ");
97+
if (numberpos == SCP_string::npos)
98+
return get_number_of_physical_cores_fallback();
99+
100+
int num_cores = std::stoi(line.substr(numberpos + 2));
101+
102+
if (num_cores < 1) {
103+
//invalid results, try fallback
104+
return get_number_of_physical_cores_fallback();
105+
}
106+
else {
107+
return num_cores;
108+
}
109+
}
110+
}
111+
return get_number_of_physical_cores_fallback();
112+
}
113+
catch (const std::exception&) {
114+
return get_number_of_physical_cores_fallback();
115+
}
116+
}
117+
#else
118+
#define get_number_of_physical_cores() get_number_of_physical_cores_fallback()
119+
#endif
120+
40121
//External Functions
41122

42123
void spin_up_threaded_task(WorkerThreadTask task) {
@@ -54,12 +135,19 @@ namespace threading {
54135
}
55136

56137
void init_task_pool() {
138+
if (Cmdline_multithreading == 0) {
139+
num_threads = get_number_of_physical_cores() - 1;
140+
}
141+
else {
142+
num_threads = Cmdline_multithreading - 1;
143+
}
144+
57145
if (!is_threading())
58146
return;
59147

60-
mprintf(("Spinning up threadpool with %d threads...\n", Cmdline_multithreading - 1));
148+
mprintf(("Spinning up threadpool with %d threads...\n", static_cast<int>(num_threads)));
61149

62-
for (size_t i = 0; i < static_cast<size_t>(Cmdline_multithreading - 1); i++) {
150+
for (size_t i = 0; i < num_threads; i++) {
63151
worker_threads.emplace_back([i](){ mp_worker_thread_main(i); });
64152
}
65153
}
@@ -73,7 +161,7 @@ namespace threading {
73161
}
74162

75163
bool is_threading() {
76-
return Cmdline_multithreading > 1;
164+
return num_threads > 0;
77165
}
78166

79167
size_t get_num_workers() {

0 commit comments

Comments
 (0)