|
|
|
|
@ -86,6 +86,18 @@ struct other_thread {
|
|
|
|
|
struct stats stats; // temp storage, owned by output thread
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct freq_stats {
|
|
|
|
|
long min;
|
|
|
|
|
long max;
|
|
|
|
|
long sum;
|
|
|
|
|
long samples;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct thread_freq_stats {
|
|
|
|
|
mutex_t lock;
|
|
|
|
|
struct freq_stats stats;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef void render_fn(const struct stats *stats, int line, int x, int breadth, int width,
|
|
|
|
|
int color,
|
|
|
|
|
@ -141,6 +153,11 @@ static int init_threads = 0;
|
|
|
|
|
static bool bidirectional = false;
|
|
|
|
|
static int max_cpu = 0;
|
|
|
|
|
static bool system_cpu;
|
|
|
|
|
static int break_in = 200;
|
|
|
|
|
static int measure_time = 500;
|
|
|
|
|
static int repeats = 1;
|
|
|
|
|
static bool cpu_freq;
|
|
|
|
|
static int freq_granularity = 50;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define BLOCKED_COLOR 1
|
|
|
|
|
@ -1512,6 +1529,36 @@ static void options(int *argc, char ***argv) {
|
|
|
|
|
.arg_data = &system_cpu,
|
|
|
|
|
.description = "Consider system CPU usage for automated tests",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.long_name = "break-in",
|
|
|
|
|
.arg = G_OPTION_ARG_INT,
|
|
|
|
|
.arg_data = &break_in,
|
|
|
|
|
.description = "Break-in time in ms before measuring for automated tests",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.long_name = "measure-time",
|
|
|
|
|
.arg = G_OPTION_ARG_INT,
|
|
|
|
|
.arg_data = &measure_time,
|
|
|
|
|
.description = "Duration of automated tests in ms",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.long_name = "repeats",
|
|
|
|
|
.arg = G_OPTION_ARG_INT,
|
|
|
|
|
.arg_data = &repeats,
|
|
|
|
|
.description = "Number of times to repeat automated test",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.long_name = "cpu-freq",
|
|
|
|
|
.arg = G_OPTION_ARG_NONE,
|
|
|
|
|
.arg_data = &cpu_freq,
|
|
|
|
|
.description = "Monitor CPU frequencies during automated test",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.long_name = "freq-granularity",
|
|
|
|
|
.arg = G_OPTION_ARG_INT,
|
|
|
|
|
.arg_data = &freq_granularity,
|
|
|
|
|
.description = "Granularity in ms for measuring CPU frequencies",
|
|
|
|
|
},
|
|
|
|
|
{ NULL, }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@ -1525,6 +1572,8 @@ static void options(int *argc, char ***argv) {
|
|
|
|
|
|
|
|
|
|
if (max_cpu > 100 || max_cpu < 0)
|
|
|
|
|
die("Invalid `max-cpu` number given");
|
|
|
|
|
if (freq_granularity <= 0)
|
|
|
|
|
die("Invalid `freq-granularity` number given");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1582,6 +1631,63 @@ static void delay_measure_workers(uint milliseconds, struct stats *totals) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void *cpu_freq_monitor(void *p) {
|
|
|
|
|
struct thread_freq_stats *freq_stats = p;
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
struct freq_stats iter_stats = {0};
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
AUTO_CLEANUP(DIR *dp, closedir_p) = opendir("/sys/devices/system/cpu/cpufreq");
|
|
|
|
|
if (!dp)
|
|
|
|
|
break; // bail out
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct dirent *ent;
|
|
|
|
|
while ((ent = readdir(dp))) {
|
|
|
|
|
if (strncmp(ent->d_name, "policy", 6) != 0)
|
|
|
|
|
continue; // skip
|
|
|
|
|
|
|
|
|
|
AUTO_CLEANUP(char *fn, free_gbuf)
|
|
|
|
|
= g_strdup_printf("/sys/devices/system/cpu/cpufreq/%s/scaling_cur_freq",
|
|
|
|
|
ent->d_name);
|
|
|
|
|
AUTO_CLEANUP(FILE *fp, fclose_p) = fopen(fn, "r");
|
|
|
|
|
if (!fp)
|
|
|
|
|
continue; // ignore
|
|
|
|
|
|
|
|
|
|
long long freq;
|
|
|
|
|
int rets = fscanf(fp, "%lld", &freq);
|
|
|
|
|
if (rets != 1)
|
|
|
|
|
continue; // ignore
|
|
|
|
|
|
|
|
|
|
iter_stats.max = iter_stats.max ? MAX(iter_stats.max, freq) : freq;
|
|
|
|
|
iter_stats.min = iter_stats.min ? MIN(iter_stats.min, freq) : freq;
|
|
|
|
|
iter_stats.sum += freq;
|
|
|
|
|
iter_stats.samples++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// done collecting, add to shared struct
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
LOCK(&freq_stats->lock);
|
|
|
|
|
freq_stats->stats.max = freq_stats->stats.max
|
|
|
|
|
? MAX(freq_stats->stats.max, iter_stats.max) : iter_stats.max;
|
|
|
|
|
freq_stats->stats.min = freq_stats->stats.min
|
|
|
|
|
? MIN(freq_stats->stats.min, iter_stats.min) : iter_stats.min;
|
|
|
|
|
freq_stats->stats.sum += iter_stats.sum;
|
|
|
|
|
freq_stats->stats.samples += iter_stats.samples;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread_cancel_enable();
|
|
|
|
|
usleep(freq_granularity * 1000);
|
|
|
|
|
thread_cancel_disable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void max_cpu_test(void) {
|
|
|
|
|
int max_cpu_scaled = max_cpu * 100000;
|
|
|
|
|
|
|
|
|
|
@ -1590,17 +1696,24 @@ static void max_cpu_test(void) {
|
|
|
|
|
uint test_num = 1;
|
|
|
|
|
set_streams(test_num);
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
pthread_t cpu_thread = 0;
|
|
|
|
|
struct thread_freq_stats freq_stats = {.lock = MUTEX_STATIC_INIT};
|
|
|
|
|
if (cpu_freq)
|
|
|
|
|
cpu_thread = thread_new("CPU freq", cpu_freq_monitor, &freq_stats);
|
|
|
|
|
|
|
|
|
|
int count = repeats;
|
|
|
|
|
|
|
|
|
|
while (count > 0) {
|
|
|
|
|
// initial break-in
|
|
|
|
|
delay_measure_workers(200, NULL);
|
|
|
|
|
delay_measure_workers(break_in, NULL);
|
|
|
|
|
cpu_collect(NULL, NULL);
|
|
|
|
|
|
|
|
|
|
// measure
|
|
|
|
|
struct stats totals = {0};
|
|
|
|
|
if (!system_cpu)
|
|
|
|
|
delay_measure_workers(500, &totals);
|
|
|
|
|
delay_measure_workers(measure_time, &totals);
|
|
|
|
|
else {
|
|
|
|
|
delay_measure_workers(500, NULL);
|
|
|
|
|
delay_measure_workers(measure_time, NULL);
|
|
|
|
|
cpu_collect(NULL, &totals);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -1632,7 +1745,28 @@ static void max_cpu_test(void) {
|
|
|
|
|
? "bidirectional calls"
|
|
|
|
|
: "unidirectional streams",
|
|
|
|
|
workers.length);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (cpu_freq) {
|
|
|
|
|
// retrieve stats and reset
|
|
|
|
|
struct freq_stats stats;
|
|
|
|
|
{
|
|
|
|
|
LOCK(&freq_stats.lock);
|
|
|
|
|
stats = freq_stats.stats;
|
|
|
|
|
freq_stats.stats = (__typeof__(freq_stats.stats)) {0};
|
|
|
|
|
}
|
|
|
|
|
if (!stats.samples)
|
|
|
|
|
printf(" (no CPU frequency stats collected)\n");
|
|
|
|
|
else {
|
|
|
|
|
printf(" CPU frequencies: "
|
|
|
|
|
"%.2f <> %.2f GHz, avg %.2f GHz\n",
|
|
|
|
|
(float) stats.min / 1000000.,
|
|
|
|
|
(float) stats.max / 1000000.,
|
|
|
|
|
(float) stats.sum / (float) stats.samples
|
|
|
|
|
/ 1000000.);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
count--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// scale to 50..100
|
|
|
|
|
@ -1643,6 +1777,11 @@ static void max_cpu_test(void) {
|
|
|
|
|
|
|
|
|
|
set_streams(test_num);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cpu_thread) {
|
|
|
|
|
pthread_cancel(cpu_thread);
|
|
|
|
|
pthread_join(cpu_thread, NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|