162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*  cpufreq-bench CPUFreq microbenchmark
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *  Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <stdio.h>
862306a36Sopenharmony_ci#include <unistd.h>
962306a36Sopenharmony_ci#include <math.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "config.h"
1262306a36Sopenharmony_ci#include "system.h"
1362306a36Sopenharmony_ci#include "benchmark.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/* Print out progress if we log into a file */
1662306a36Sopenharmony_ci#define show_progress(total_time, progress_time)	\
1762306a36Sopenharmony_ciif (config->output != stdout) {				\
1862306a36Sopenharmony_ci	fprintf(stdout, "Progress: %02lu %%\r",		\
1962306a36Sopenharmony_ci		(progress_time * 100) / total_time);	\
2062306a36Sopenharmony_ci	fflush(stdout);					\
2162306a36Sopenharmony_ci}
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/**
2462306a36Sopenharmony_ci * compute how many rounds of calculation we should do
2562306a36Sopenharmony_ci * to get the given load time
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * @param load aimed load time in µs
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * @retval rounds of calculation
3062306a36Sopenharmony_ci **/
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ciunsigned int calculate_timespace(long load, struct config *config)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	int i;
3562306a36Sopenharmony_ci	long long now, then;
3662306a36Sopenharmony_ci	unsigned int estimated = GAUGECOUNT;
3762306a36Sopenharmony_ci	unsigned int rounds = 0;
3862306a36Sopenharmony_ci	unsigned int timed = 0;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (config->verbose)
4162306a36Sopenharmony_ci		printf("calibrating load of %lius, please wait...\n", load);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* get the initial calculation time for a specific number of rounds */
4462306a36Sopenharmony_ci	now = get_time();
4562306a36Sopenharmony_ci	ROUNDS(estimated);
4662306a36Sopenharmony_ci	then = get_time();
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	timed = (unsigned int)(then - now);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	/* approximation of the wanted load time by comparing with the
5162306a36Sopenharmony_ci	 * initial calculation time */
5262306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
5362306a36Sopenharmony_ci		rounds = (unsigned int)(load * estimated / timed);
5462306a36Sopenharmony_ci		dprintf("calibrating with %u rounds\n", rounds);
5562306a36Sopenharmony_ci		now = get_time();
5662306a36Sopenharmony_ci		ROUNDS(rounds);
5762306a36Sopenharmony_ci		then = get_time();
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		timed = (unsigned int)(then - now);
6062306a36Sopenharmony_ci		estimated = rounds;
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci	if (config->verbose)
6362306a36Sopenharmony_ci		printf("calibration done\n");
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return estimated;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/**
6962306a36Sopenharmony_ci * benchmark
7062306a36Sopenharmony_ci * generates a specific sleep an load time with the performance
7162306a36Sopenharmony_ci * governor and compares the used time for same calculations done
7262306a36Sopenharmony_ci * with the configured powersave governor
7362306a36Sopenharmony_ci *
7462306a36Sopenharmony_ci * @param config config values for the benchmark
7562306a36Sopenharmony_ci *
7662306a36Sopenharmony_ci **/
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_civoid start_benchmark(struct config *config)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	unsigned int _round, cycle;
8162306a36Sopenharmony_ci	long long now, then;
8262306a36Sopenharmony_ci	long sleep_time = 0, load_time = 0;
8362306a36Sopenharmony_ci	long performance_time = 0, powersave_time = 0;
8462306a36Sopenharmony_ci	unsigned int calculations;
8562306a36Sopenharmony_ci	unsigned long total_time = 0, progress_time = 0;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	sleep_time = config->sleep;
8862306a36Sopenharmony_ci	load_time = config->load;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/* For the progress bar */
9162306a36Sopenharmony_ci	for (_round = 1; _round <= config->rounds; _round++)
9262306a36Sopenharmony_ci		total_time += _round * (config->sleep + config->load);
9362306a36Sopenharmony_ci	total_time *= 2; /* powersave and performance cycles */
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	for (_round = 0; _round < config->rounds; _round++) {
9662306a36Sopenharmony_ci		performance_time = 0LL;
9762306a36Sopenharmony_ci		powersave_time = 0LL;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		show_progress(total_time, progress_time);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		/* set the cpufreq governor to "performance" which disables
10262306a36Sopenharmony_ci		 * P-State switching. */
10362306a36Sopenharmony_ci		if (set_cpufreq_governor("performance", config->cpu) != 0)
10462306a36Sopenharmony_ci			return;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		/* calibrate the calculation time. the resulting calculation
10762306a36Sopenharmony_ci		 * _rounds should produce a load which matches the configured
10862306a36Sopenharmony_ci		 * load time */
10962306a36Sopenharmony_ci		calculations = calculate_timespace(load_time, config);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		if (config->verbose)
11262306a36Sopenharmony_ci			printf("_round %i: doing %u cycles with %u calculations"
11362306a36Sopenharmony_ci			       " for %lius\n", _round + 1, config->cycles,
11462306a36Sopenharmony_ci			       calculations, load_time);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		fprintf(config->output, "%u %li %li ",
11762306a36Sopenharmony_ci			_round, load_time, sleep_time);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		if (config->verbose)
12062306a36Sopenharmony_ci			printf("average: %lius, rps:%li\n",
12162306a36Sopenharmony_ci				load_time / calculations,
12262306a36Sopenharmony_ci				1000000 * calculations / load_time);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		/* do some sleep/load cycles with the performance governor */
12562306a36Sopenharmony_ci		for (cycle = 0; cycle < config->cycles; cycle++) {
12662306a36Sopenharmony_ci			now = get_time();
12762306a36Sopenharmony_ci			usleep(sleep_time);
12862306a36Sopenharmony_ci			ROUNDS(calculations);
12962306a36Sopenharmony_ci			then = get_time();
13062306a36Sopenharmony_ci			performance_time += then - now - sleep_time;
13162306a36Sopenharmony_ci			if (config->verbose)
13262306a36Sopenharmony_ci				printf("performance cycle took %lius, "
13362306a36Sopenharmony_ci					"sleep: %lius, "
13462306a36Sopenharmony_ci					"load: %lius, rounds: %u\n",
13562306a36Sopenharmony_ci					(long)(then - now), sleep_time,
13662306a36Sopenharmony_ci					load_time, calculations);
13762306a36Sopenharmony_ci		}
13862306a36Sopenharmony_ci		fprintf(config->output, "%li ",
13962306a36Sopenharmony_ci			performance_time / config->cycles);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci		progress_time += sleep_time + load_time;
14262306a36Sopenharmony_ci		show_progress(total_time, progress_time);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci		/* set the powersave governor which activates P-State switching
14562306a36Sopenharmony_ci		 * again */
14662306a36Sopenharmony_ci		if (set_cpufreq_governor(config->governor, config->cpu) != 0)
14762306a36Sopenharmony_ci			return;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		/* again, do some sleep/load cycles with the
15062306a36Sopenharmony_ci		 * powersave governor */
15162306a36Sopenharmony_ci		for (cycle = 0; cycle < config->cycles; cycle++) {
15262306a36Sopenharmony_ci			now = get_time();
15362306a36Sopenharmony_ci			usleep(sleep_time);
15462306a36Sopenharmony_ci			ROUNDS(calculations);
15562306a36Sopenharmony_ci			then = get_time();
15662306a36Sopenharmony_ci			powersave_time += then - now - sleep_time;
15762306a36Sopenharmony_ci			if (config->verbose)
15862306a36Sopenharmony_ci				printf("powersave cycle took %lius, "
15962306a36Sopenharmony_ci					"sleep: %lius, "
16062306a36Sopenharmony_ci					"load: %lius, rounds: %u\n",
16162306a36Sopenharmony_ci					(long)(then - now), sleep_time,
16262306a36Sopenharmony_ci					load_time, calculations);
16362306a36Sopenharmony_ci		}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		progress_time += sleep_time + load_time;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		/* compare the average sleep/load cycles  */
16862306a36Sopenharmony_ci		fprintf(config->output, "%li ",
16962306a36Sopenharmony_ci			powersave_time / config->cycles);
17062306a36Sopenharmony_ci		fprintf(config->output, "%.3f\n",
17162306a36Sopenharmony_ci			performance_time * 100.0 / powersave_time);
17262306a36Sopenharmony_ci		fflush(config->output);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci		if (config->verbose)
17562306a36Sopenharmony_ci			printf("performance is at %.2f%%\n",
17662306a36Sopenharmony_ci				performance_time * 100.0 / powersave_time);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci		sleep_time += config->sleep_step;
17962306a36Sopenharmony_ci		load_time += config->load_step;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci}
182