From: Jesse Rosenstock Date: Tue, 1 Jul 2025 10:38:24 +0000 (+0200) Subject: cpuset: Use stride in cpulist_create X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9938ae4d49f64cbcfb48158d537454ac740d7dba;p=thirdparty%2Futil-linux.git cpuset: Use stride in cpulist_create Format cpu lists for `taskset -c`, `lscpu`, etc. using stride. This usually produces shorter output. Now: % ./taskset -c 0-24:2,25-47:4 ./taskset -pc 0 pid 3937653's current affinity list: 0-24:2,25-45:4 Previously: % ./taskset -c 0-23:2,24-47:4 ./taskset -pc 0 pid 3933321's current affinity list: 0,2,4,6,8,10,12,14,16,18,20,22,24,28,32,36,40,44 The ranges are constructed greedily; in general it is difficult to construct a minimal list. % ./taskset -c 0-63:3,0-63:4 ./taskset -pc 0 pid 3926213's current affinity list: 0,3,4-8:2,9-15:3,16-20:2,21-27:3,28-32:2,33-39:3,40-44:2,45 Signed-off-by: Jesse Rosenstock --- diff --git a/lib/cpuset.c b/lib/cpuset.c index 533b8ab30..d64692030 100644 --- a/lib/cpuset.c +++ b/lib/cpuset.c @@ -147,43 +147,70 @@ int __cpuset_count_s(size_t setsize, const cpu_set_t *set) } #endif +/* + * Finds the first CPU present after the specified index. + * + * start: starting index, inclusive. + * setsize: size of the set in *bytes*. + * set: CPU set to search. + * + * Return: the index of the first CPU present in `set`, starting at `start`. + * If no such CPU exists, returns the size of the set in *bits*. + */ +static size_t find_next_cpu(size_t start, size_t setsize, cpu_set_t *set) +{ + size_t nbits = cpuset_nbits(setsize); + for (; start < nbits; start++) + if (CPU_ISSET_S(start, setsize, set)) + return start; + return start; +} + /* * Returns human readable representation of the cpuset. The output format is - * a list of CPUs with ranges (for example, "0,1,3-9"). + * a list of CPUs with ranges (for example, "0,1,3-9:3"). */ char *cpulist_create(char *str, size_t len, cpu_set_t *set, size_t setsize) { - size_t i; char *ptr = str; int entry_made = 0; size_t max = cpuset_nbits(setsize); - - for (i = 0; i < max; i++) { - if (CPU_ISSET_S(i, setsize, set)) { - int rlen; - size_t j, run = 0; - entry_made = 1; - for (j = i + 1; j < max; j++) { - if (CPU_ISSET_S(j, setsize, set)) - run++; - else - break; + size_t a = 0; /* min for cpu range */ + size_t next = 0; /* where to start looking for next cpu */ + + while ((a = find_next_cpu(next, setsize, set)) < max) { + int rlen; + next = find_next_cpu(a + 1, setsize, set); + if (next == max) { + rlen = snprintf(ptr, len, "%zu,", a); + } else { + /* Extend range as long as we have the same stride. */ + size_t b = next; + size_t s = b - a; + while (((next = find_next_cpu(b + 1, setsize, set)) < + max) && next - b == s) { + b = next; } - if (!run) - rlen = snprintf(ptr, len, "%zu,", i); - else if (run == 1) { - rlen = snprintf(ptr, len, "%zu,%zu,", i, i + 1); - i++; + if (b - a == s) { + /* + * Only print one CPU. Hope the next one can + * be put in the next range. + */ + rlen = snprintf(ptr, len, "%zu,", a); + next = b; + } else if (s == 1) { + rlen = snprintf(ptr, len, "%zu-%zu,", a, b); } else { - rlen = snprintf(ptr, len, "%zu-%zu,", i, i + run); - i += run; + rlen = snprintf(ptr, len, "%zu-%zu:%zu,", + a, b, s); } - if (rlen < 0 || (size_t) rlen >= len) - return NULL; - ptr += rlen; - len -= rlen; } + if (rlen < 0 || (size_t) rlen >= len) + return NULL; + ptr += rlen; + len -= rlen; + entry_made = 1; } ptr -= entry_made; *ptr = '\0'; diff --git a/tests/expected/lscpu/lscpu-x86_64-64cpu b/tests/expected/lscpu/lscpu-x86_64-64cpu index b5b6c85c3..d359a6875 100644 --- a/tests/expected/lscpu/lscpu-x86_64-64cpu +++ b/tests/expected/lscpu/lscpu-x86_64-64cpu @@ -21,9 +21,9 @@ L1i cache: 1 MiB (32 instances) L2 cache: 8 MiB (32 instances) L3 cache: 72 MiB (4 instances) NUMA node(s): 3 -NUMA node0 CPU(s): 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62 -NUMA node2 CPU(s): 1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61 -NUMA node3 CPU(s): 3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63 +NUMA node0 CPU(s): 0-62:2 +NUMA node2 CPU(s): 1-61:4 +NUMA node3 CPU(s): 3-63:4 # The following is the parsable format, which can be fed to other # programs. Each different item in every column has an unique ID diff --git a/tests/expected/misc/bits-list b/tests/expected/misc/bits-list index 7511e5378..e6c6d142a 100644 --- a/tests/expected/misc/bits-list +++ b/tests/expected/misc/bits-list @@ -1 +1 @@ -11,22,33,44 +11-44:11 diff --git a/tests/expected/misc/bits-parse-mask b/tests/expected/misc/bits-parse-mask index 59dd4b4c1..37b1257b8 100644 --- a/tests/expected/misc/bits-parse-mask +++ b/tests/expected/misc/bits-parse-mask @@ -1 +1 @@ -1,3,6,7,9,11,14-16,18,19,21,23-25,27 +1,3,6,7-11:2,14-16,18,19-23:2,24,25,27 diff --git a/tests/expected/schedutils/cpuset b/tests/expected/schedutils/cpuset index 10e17606c..695e75dc9 100644 --- a/tests/expected/schedutils/cpuset +++ b/tests/expected/schedutils/cpuset @@ -8,7 +8,7 @@ masks: 0x00000007 = 7 [0-2] 0x00000008 = 8 [3] 0x00000009 = 9 [0,3] -0x00005555 = 5555 [0,2,4,6,8,10,12,14] +0x00005555 = 5555 [0-14:2] 0x00007777 = 7777 [0-2,4-6,8-10,12-14] strings: 0 = 1 [0] @@ -20,5 +20,5 @@ strings: 0-2 = 7 [0-2] 3 = 8 [3] 0,3 = 9 [0,3] -0,2,4,6,8,10,12,14 = 5555 [0,2,4,6,8,10,12,14] +0,2,4,6,8,10,12,14 = 5555 [0-14:2] 0-2,4-6,8-10,12-14 = 7777 [0-2,4-6,8-10,12-14]