perf smt: Compute SMT from topology
The topology records sibling threads. Rather than computing SMT using siblings in sysfs, reuse the values in topology. This only applies when the file smt/active isn't available. Signed-off-by: Ian Rogers <irogers@google.com> Cc: Ahmad Yasin <ahmad.yasin@intel.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: Caleb Biggers <caleb.biggers@intel.com> Cc: Florian Fischer <florian.fischer@muhq.space> Cc: Ingo Molnar <mingo@redhat.com> Cc: James Clark <james.clark@arm.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: John Garry <john.garry@huawei.com> Cc: Kan Liang <kan.liang@linux.intel.com> Cc: Kshipra Bopardikar <kshipra.bopardikar@intel.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Miaoqian Lin <linmq006@gmail.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Perry Taylor <perry.taylor@intel.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Thomas Richter <tmricht@linux.ibm.com> Cc: Xing Zhengjun <zhengjun.xing@linux.intel.com> Link: https://lore.kernel.org/r/20220831174926.579643-4-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
committed by
Arnaldo Carvalho de Melo
parent
1a6abdde13
commit
09b73fe9e3
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#include "util/cputopo.h"
|
||||||
#include "util/debug.h"
|
#include "util/debug.h"
|
||||||
#include "util/expr.h"
|
#include "util/expr.h"
|
||||||
#include "util/header.h"
|
#include "util/header.h"
|
||||||
@@ -154,15 +155,20 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u
|
|||||||
(void **)&val_ptr));
|
(void **)&val_ptr));
|
||||||
|
|
||||||
/* Only EVENT1 or EVENT2 need be measured depending on the value of smt_on. */
|
/* Only EVENT1 or EVENT2 need be measured depending on the value of smt_on. */
|
||||||
expr__ctx_clear(ctx);
|
{
|
||||||
TEST_ASSERT_VAL("find ids",
|
struct cpu_topology *topology = cpu_topology__new();
|
||||||
expr__find_ids("EVENT1 if #smt_on else EVENT2",
|
bool smton = smt_on(topology);
|
||||||
NULL, ctx) == 0);
|
|
||||||
TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 1);
|
|
||||||
TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids,
|
|
||||||
smt_on() ? "EVENT1" : "EVENT2",
|
|
||||||
(void **)&val_ptr));
|
|
||||||
|
|
||||||
|
cpu_topology__delete(topology);
|
||||||
|
expr__ctx_clear(ctx);
|
||||||
|
TEST_ASSERT_VAL("find ids",
|
||||||
|
expr__find_ids("EVENT1 if #smt_on else EVENT2",
|
||||||
|
NULL, ctx) == 0);
|
||||||
|
TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 1);
|
||||||
|
TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids,
|
||||||
|
smton ? "EVENT1" : "EVENT2",
|
||||||
|
(void **)&val_ptr));
|
||||||
|
}
|
||||||
/* The expression is a constant 1.0 without needing to evaluate EVENT1. */
|
/* The expression is a constant 1.0 without needing to evaluate EVENT1. */
|
||||||
expr__ctx_clear(ctx);
|
expr__ctx_clear(ctx);
|
||||||
TEST_ASSERT_VAL("find ids",
|
TEST_ASSERT_VAL("find ids",
|
||||||
|
|||||||
@@ -157,6 +157,21 @@ void cpu_topology__delete(struct cpu_topology *tp)
|
|||||||
free(tp);
|
free(tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cpu_topology__smt_on(const struct cpu_topology *topology)
|
||||||
|
{
|
||||||
|
for (u32 i = 0; i < topology->core_cpus_lists; i++) {
|
||||||
|
const char *cpu_list = topology->core_cpus_list[i];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there is a need to separate siblings in a core then SMT is
|
||||||
|
* enabled.
|
||||||
|
*/
|
||||||
|
if (strchr(cpu_list, ',') || strchr(cpu_list, '-'))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static bool has_die_topology(void)
|
static bool has_die_topology(void)
|
||||||
{
|
{
|
||||||
char filename[MAXPATHLEN];
|
char filename[MAXPATHLEN];
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ struct hybrid_topology {
|
|||||||
|
|
||||||
struct cpu_topology *cpu_topology__new(void);
|
struct cpu_topology *cpu_topology__new(void);
|
||||||
void cpu_topology__delete(struct cpu_topology *tp);
|
void cpu_topology__delete(struct cpu_topology *tp);
|
||||||
|
/* Determine from the core list whether SMT was enabled. */
|
||||||
|
bool cpu_topology__smt_on(const struct cpu_topology *topology);
|
||||||
|
|
||||||
struct numa_topology *numa_topology__new(void);
|
struct numa_topology *numa_topology__new(void);
|
||||||
void numa_topology__delete(struct numa_topology *tp);
|
void numa_topology__delete(struct numa_topology *tp);
|
||||||
|
|||||||
@@ -412,11 +412,6 @@ double expr__get_literal(const char *literal)
|
|||||||
static struct cpu_topology *topology;
|
static struct cpu_topology *topology;
|
||||||
double result = NAN;
|
double result = NAN;
|
||||||
|
|
||||||
if (!strcasecmp("#smt_on", literal)) {
|
|
||||||
result = smt_on() > 0 ? 1.0 : 0.0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp("#num_cpus", literal)) {
|
if (!strcmp("#num_cpus", literal)) {
|
||||||
result = cpu__max_present_cpu().cpu;
|
result = cpu__max_present_cpu().cpu;
|
||||||
goto out;
|
goto out;
|
||||||
@@ -440,6 +435,10 @@ double expr__get_literal(const char *literal)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!strcasecmp("#smt_on", literal)) {
|
||||||
|
result = smt_on(topology) ? 1.0 : 0.0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
if (!strcmp("#num_packages", literal)) {
|
if (!strcmp("#num_packages", literal)) {
|
||||||
result = topology->package_cpus_lists;
|
result = topology->package_cpus_lists;
|
||||||
goto out;
|
goto out;
|
||||||
|
|||||||
@@ -1,100 +1,23 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
#include <stdio.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <linux/bitops.h>
|
|
||||||
#include "api/fs/fs.h"
|
#include "api/fs/fs.h"
|
||||||
|
#include "cputopo.h"
|
||||||
#include "smt.h"
|
#include "smt.h"
|
||||||
|
|
||||||
/**
|
bool smt_on(const struct cpu_topology *topology)
|
||||||
* hweight_str - Returns the number of bits set in str. Stops at first non-hex
|
|
||||||
* or ',' character.
|
|
||||||
*/
|
|
||||||
static int hweight_str(char *str)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
while (*str) {
|
|
||||||
switch (*str++) {
|
|
||||||
case '0':
|
|
||||||
case ',':
|
|
||||||
break;
|
|
||||||
case '1':
|
|
||||||
case '2':
|
|
||||||
case '4':
|
|
||||||
case '8':
|
|
||||||
result++;
|
|
||||||
break;
|
|
||||||
case '3':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '9':
|
|
||||||
case 'a':
|
|
||||||
case 'A':
|
|
||||||
case 'c':
|
|
||||||
case 'C':
|
|
||||||
result += 2;
|
|
||||||
break;
|
|
||||||
case '7':
|
|
||||||
case 'b':
|
|
||||||
case 'B':
|
|
||||||
case 'd':
|
|
||||||
case 'D':
|
|
||||||
case 'e':
|
|
||||||
case 'E':
|
|
||||||
result += 3;
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
case 'F':
|
|
||||||
result += 4;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
done:
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int smt_on(void)
|
|
||||||
{
|
{
|
||||||
static bool cached;
|
static bool cached;
|
||||||
static int cached_result;
|
static bool cached_result;
|
||||||
int cpu;
|
int fs_value;
|
||||||
int ncpu;
|
|
||||||
|
|
||||||
if (cached)
|
if (cached)
|
||||||
return cached_result;
|
return cached_result;
|
||||||
|
|
||||||
if (sysfs__read_int("devices/system/cpu/smt/active", &cached_result) >= 0) {
|
if (sysfs__read_int("devices/system/cpu/smt/active", &fs_value) >= 0)
|
||||||
cached = true;
|
cached_result = (fs_value == 1);
|
||||||
return cached_result;
|
else
|
||||||
}
|
cached_result = cpu_topology__smt_on(topology);
|
||||||
|
|
||||||
cached_result = 0;
|
|
||||||
ncpu = sysconf(_SC_NPROCESSORS_CONF);
|
|
||||||
for (cpu = 0; cpu < ncpu; cpu++) {
|
|
||||||
unsigned long long siblings;
|
|
||||||
char *str;
|
|
||||||
size_t strlen;
|
|
||||||
char fn[256];
|
|
||||||
|
|
||||||
snprintf(fn, sizeof fn,
|
|
||||||
"devices/system/cpu/cpu%d/topology/thread_siblings", cpu);
|
|
||||||
if (sysfs__read_str(fn, &str, &strlen) < 0) {
|
|
||||||
snprintf(fn, sizeof fn,
|
|
||||||
"devices/system/cpu/cpu%d/topology/core_cpus", cpu);
|
|
||||||
if (sysfs__read_str(fn, &str, &strlen) < 0)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* Entry is hex, but does not have 0x, so need custom parser */
|
|
||||||
siblings = hweight_str(str);
|
|
||||||
free(str);
|
|
||||||
if (siblings > 1) {
|
|
||||||
cached_result = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cached = true;
|
cached = true;
|
||||||
return cached_result;
|
return cached_result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
#ifndef __SMT_H
|
#ifndef __SMT_H
|
||||||
#define __SMT_H 1
|
#define __SMT_H 1
|
||||||
|
|
||||||
int smt_on(void);
|
struct cpu_topology;
|
||||||
|
|
||||||
|
/* Returns true if SMT (aka hyperthreading) is enabled. */
|
||||||
|
bool smt_on(const struct cpu_topology *topology);
|
||||||
|
|
||||||
#endif /* __SMT_H */
|
#endif /* __SMT_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user