MEDIUM: threads: detect excessive thread counts vs cpu-map

This detects when there are more threads bound via cpu-map than CPUs
enabled in cpu-map, or when there are more total threads than the total
number of CPUs available at boot (for unbound threads) and configured
for bound threads. In this case, a warning is emitted to explain the
problems it will cause, and explaining how to address the situation.

Note that some configurations will not be detected as faulty because
the algorithmic complexity to resolve all arrangements grows in O(N!).
This means that having 3 threads on 2 CPUs and one thread on 2 CPUs
will not be detected as it's 4 threads for 4 CPUs. But at least configs
such as T0:(1,4) T1:(1,4) T2:(2,4) T3:(3,4) will not trigger a warning
since they're valid.
This commit is contained in:
Willy Tarreau 2023-09-04 17:36:20 +02:00
parent 8357f950cb
commit 86854dd032
3 changed files with 58 additions and 0 deletions

View File

@ -44,6 +44,7 @@ void ha_tkill(unsigned int thr, int sig);
void ha_tkillall(int sig);
void ha_thread_relax(void);
int thread_detect_binding_discrepancies(void);
int thread_detect_more_than_cpus(void);
int thread_map_to_groups();
int thread_resolve_group_mask(struct thread_set *ts, int defgrp, char **err);
int parse_thread_set(const char *arg, struct thread_set *ts, char **err);

View File

@ -2311,6 +2311,7 @@ static void init(int argc, char **argv)
#endif
thread_detect_binding_discrepancies();
thread_detect_more_than_cpus();
/* Apply server states */
apply_server_state();

View File

@ -1171,6 +1171,62 @@ int thread_detect_binding_discrepancies(void)
return 0;
}
/* Returns non-zero on anomaly (more threads than CPUs), and emits a warning in
* this case. It checks against configured cpu-map if any, otherwise against
* the number of CPUs at boot if known. It's better to run it only after
* thread_detect_binding_discrepancies() so that mixed cases can be eliminated.
*/
int thread_detect_more_than_cpus(void)
{
#if defined(USE_CPU_AFFINITY)
struct hap_cpuset cpuset_map, cpuset_boot, cpuset_all;
uint th, tg, id;
int bound;
int tot_map, tot_all;
ha_cpuset_zero(&cpuset_boot);
ha_cpuset_zero(&cpuset_map);
ha_cpuset_zero(&cpuset_all);
bound = 0;
for (th = 0; th < global.nbthread; th++) {
tg = ha_thread_info[th].tgid;
id = ha_thread_info[th].ltid;
if (ha_cpuset_count(&cpu_map[tg - 1].thread[id])) {
ha_cpuset_or(&cpuset_map, &cpu_map[tg - 1].thread[id]);
bound++;
}
}
ha_cpuset_assign(&cpuset_all, &cpuset_map);
if (bound != global.nbthread) {
if (ha_cpuset_detect_bound(&cpuset_boot))
ha_cpuset_or(&cpuset_all, &cpuset_boot);
}
tot_map = ha_cpuset_count(&cpuset_map);
tot_all = ha_cpuset_count(&cpuset_all);
if (tot_map && bound > tot_map) {
ha_warning("This configuration binds %d threads to a total of %d CPUs via cpu-map "
"directives. This means that some threads will compete for the same CPU, "
"which will cause severe performance degradation. Please fix either the "
"'cpu-map' directives or set the global 'nbthread' value accordingly.\n",
bound, tot_map);
return 1;
}
else if (tot_all && global.nbthread > tot_all) {
ha_warning("This configuration enables %d threads running on a total of %d CPUs. "
"This means that some threads will compete for the same CPU, which will cause "
"severe performance degradation. Please either the 'cpu-map' directives to "
"adjust the CPUs to use, or fix the global 'nbthread' value.\n",
global.nbthread, tot_all);
return 1;
}
#endif
return 0;
}
/* scans the configured thread mapping and establishes the final one. Returns <0
* on failure, >=0 on success.
*/