[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <CAP_z_Cg1Yvyq1qEkYXcyL-=zqTxqMxpV3Ap7ggzd4Q8f+=Zd9A@mail.gmail.com>
Date: Wed, 30 Jul 2025 14:05:18 -0700
From: Blake Jones <blakejones@...gle.com>
To: Ingo Molnar <mingo@...hat.com>, Peter Zijlstra <peterz@...radead.org>,
Juri Lelli <juri.lelli@...hat.com>, Vincent Guittot <vincent.guittot@...aro.org>
Cc: Madadi Vineeth Reddy <vineethr@...ux.ibm.com>, Josh Don <joshdon@...gle.com>,
Dietmar Eggemann <dietmar.eggemann@....com>, Steven Rostedt <rostedt@...dmis.org>,
Ben Segall <bsegall@...gle.com>, Mel Gorman <mgorman@...e.de>,
Valentin Schneider <vschneid@...hat.com>, linux-kernel@...r.kernel.org
Subject: Re: [PATCH v2] Reorder some fields in struct rq.
Below is the source for the load test that I used. Although it
performs some timing calculations, the actual metric I used to
evaluate the change was "average amount of CPU time spent in
sched_balance_rq()", as described above.
------------------------------------------------------------------------
#include <poll.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
struct {
/* "-m": Number of milliseconds to sleep for. */
int sleep_ms;
/* "-l": (base 2) Log of number of sleeps to do. */
int log_loops;
/* "-c": Number of children. */
int children;
} params = {
.sleep_ms = 1,
.log_loops = 13,
.children = 1000,
};
/* ------------------------------------------------------------------ */
typedef struct {
pthread_mutex_t mutex;
pthread_cond_t cv;
pthread_cond_t parent_cv;
int nthreads;
int nthreads_total;
int go;
int stop;
} thread_group_t;
void
thread_data_init(thread_group_t *tg)
{
pthread_mutexattr_t mattr;
pthread_mutexattr_init(&mattr);
pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&tg->mutex, &mattr);
pthread_mutexattr_destroy(&mattr);
pthread_condattr_t cattr;
pthread_condattr_init(&cattr);
pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(&tg->cv, &cattr);
pthread_cond_init(&tg->parent_cv, &cattr);
pthread_condattr_destroy(&cattr);
tg->nthreads = 0;
tg->nthreads_total = params.children;
tg->go = 0;
tg->stop = 0;
}
void
parent_thread(thread_group_t *tg)
{
pthread_mutex_lock(&tg->mutex);
while (tg->nthreads != tg->nthreads_total) {
pthread_cond_wait(&tg->parent_cv, &tg->mutex);
}
tg->go = 1;
pthread_cond_broadcast(&tg->cv);
pthread_mutex_unlock(&tg->mutex);
pthread_mutex_lock(&tg->mutex);
while (tg->nthreads != 0) {
pthread_cond_wait(&tg->parent_cv, &tg->mutex);
}
tg->stop = 1;
pthread_cond_broadcast(&tg->cv);
pthread_mutex_unlock(&tg->mutex);
}
void
loop(unsigned long long loops)
{
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = params.sleep_ms * 1000000;
for (unsigned long long i = 0; i < loops; i++) {
nanosleep(&ts, NULL);
}
}
void *
child_thread(thread_group_t *tg)
{
pthread_mutex_lock(&tg->mutex);
++tg->nthreads;
if (tg->nthreads == tg->nthreads_total) {
pthread_cond_signal(&tg->parent_cv);
}
while (!tg->go) {
pthread_cond_wait(&tg->cv, &tg->mutex);
}
pthread_mutex_unlock(&tg->mutex);
loop(1ULL << params.log_loops);
pthread_mutex_lock(&tg->mutex);
--tg->nthreads;
if (tg->nthreads == 0) {
pthread_cond_signal(&tg->parent_cv);
}
while (!tg->stop) {
pthread_cond_wait(&tg->cv, &tg->mutex);
}
pthread_mutex_unlock(&tg->mutex);
return NULL;
}
void
thread_data_fini(thread_group_t *tg)
{
pthread_mutex_destroy(&tg->mutex);
pthread_cond_destroy(&tg->cv);
pthread_cond_destroy(&tg->parent_cv);
}
/* ------------------------------------------------------------------ */
void *
spawn_procs(thread_group_t *tg)
{
pid_t *pids = malloc(params.children * sizeof (pid_t));
for (int c = 0; c < params.children; c++) {
pid_t pid;
switch (pid = fork()) {
case 0:
child_thread(tg);
_exit(0);
break;
case -1:
perror("fork() failed");
return NULL;
default:
pids[c] = pid;
break;
}
}
return pids;
}
void
await_procs(void *data)
{
pid_t *pids = (pid_t *)data;
for (int c = 0; c < params.children; c++) {
int rv = waitpid(pids[c], NULL, 0);
if (rv != pids[c]) {
char msg[256];
snprintf(msg, sizeof(msg),
"waitpid(%d) = %d", pids[c], rv);
perror(msg);
}
}
free(pids);
}
double
main_loop(void)
{
void *mem = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (mem == MAP_FAILED) {
perror("mmap() failed");
return 0;
}
thread_group_t *tg = mem;
thread_data_init(tg);
void *data = spawn_procs(tg);
struct timespec ts1, ts2;
if (clock_gettime(CLOCK_MONOTONIC, &ts1) == -1) {
perror("clock_gettime(ts1)");
return 1;
}
parent_thread(tg);
if (clock_gettime(CLOCK_MONOTONIC, &ts2) == -1) {
perror("clock_gettime(ts2)");
return 1;
}
await_procs(data);
thread_data_fini(tg);
munmap(mem, 4096);
double t1 = (double)ts1.tv_sec + (double)ts1.tv_nsec / 1e9;
double t2 = (double)ts2.tv_sec + (double)ts2.tv_nsec / 1e9;
return t2 - t1;
}
int
main(int argc, char **argv)
{
int opt;
while ((opt = getopt(argc, argv, "m:l:c:f")) != -1) {
switch (opt) {
case 'm':
params.sleep_ms = atoi(optarg);
break;
case 'l':
params.log_loops = atoi(optarg);
break;
case 'c':
params.children = atoi(optarg);
break;
default:
fprintf(stderr, "Usage: "
"%s [-m <ms> -l <log> -c <children>]\n", argv[0]);
return 1;
}
}
printf("Running: "
"%5d children, 1<<%d loops, %d ms sleep/loop\n",
params.children, params.log_loops, params.sleep_ms);
printf("%.5lf\n", main_loop());
return 0;
}
Powered by blists - more mailing lists