18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*  cpufreq-bench CPUFreq microbenchmark
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *  Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <stdio.h>
88c2ecf20Sopenharmony_ci#include <unistd.h>
98c2ecf20Sopenharmony_ci#include <math.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "config.h"
128c2ecf20Sopenharmony_ci#include "system.h"
138c2ecf20Sopenharmony_ci#include "benchmark.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/* Print out progress if we log into a file */
168c2ecf20Sopenharmony_ci#define show_progress(total_time, progress_time)	\
178c2ecf20Sopenharmony_ciif (config->output != stdout) {				\
188c2ecf20Sopenharmony_ci	fprintf(stdout, "Progress: %02lu %%\r",		\
198c2ecf20Sopenharmony_ci		(progress_time * 100) / total_time);	\
208c2ecf20Sopenharmony_ci	fflush(stdout);					\
218c2ecf20Sopenharmony_ci}
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/**
248c2ecf20Sopenharmony_ci * compute how many rounds of calculation we should do
258c2ecf20Sopenharmony_ci * to get the given load time
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * @param load aimed load time in µs
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * @retval rounds of calculation
308c2ecf20Sopenharmony_ci **/
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ciunsigned int calculate_timespace(long load, struct config *config)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	int i;
358c2ecf20Sopenharmony_ci	long long now, then;
368c2ecf20Sopenharmony_ci	unsigned int estimated = GAUGECOUNT;
378c2ecf20Sopenharmony_ci	unsigned int rounds = 0;
388c2ecf20Sopenharmony_ci	unsigned int timed = 0;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	if (config->verbose)
418c2ecf20Sopenharmony_ci		printf("calibrating load of %lius, please wait...\n", load);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/* get the initial calculation time for a specific number of rounds */
448c2ecf20Sopenharmony_ci	now = get_time();
458c2ecf20Sopenharmony_ci	ROUNDS(estimated);
468c2ecf20Sopenharmony_ci	then = get_time();
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	timed = (unsigned int)(then - now);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	/* approximation of the wanted load time by comparing with the
518c2ecf20Sopenharmony_ci	 * initial calculation time */
528c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
538c2ecf20Sopenharmony_ci		rounds = (unsigned int)(load * estimated / timed);
548c2ecf20Sopenharmony_ci		dprintf("calibrating with %u rounds\n", rounds);
558c2ecf20Sopenharmony_ci		now = get_time();
568c2ecf20Sopenharmony_ci		ROUNDS(rounds);
578c2ecf20Sopenharmony_ci		then = get_time();
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci		timed = (unsigned int)(then - now);
608c2ecf20Sopenharmony_ci		estimated = rounds;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci	if (config->verbose)
638c2ecf20Sopenharmony_ci		printf("calibration done\n");
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	return estimated;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/**
698c2ecf20Sopenharmony_ci * benchmark
708c2ecf20Sopenharmony_ci * generates a specific sleep an load time with the performance
718c2ecf20Sopenharmony_ci * governor and compares the used time for same calculations done
728c2ecf20Sopenharmony_ci * with the configured powersave governor
738c2ecf20Sopenharmony_ci *
748c2ecf20Sopenharmony_ci * @param config config values for the benchmark
758c2ecf20Sopenharmony_ci *
768c2ecf20Sopenharmony_ci **/
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_civoid start_benchmark(struct config *config)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	unsigned int _round, cycle;
818c2ecf20Sopenharmony_ci	long long now, then;
828c2ecf20Sopenharmony_ci	long sleep_time = 0, load_time = 0;
838c2ecf20Sopenharmony_ci	long performance_time = 0, powersave_time = 0;
848c2ecf20Sopenharmony_ci	unsigned int calculations;
858c2ecf20Sopenharmony_ci	unsigned long total_time = 0, progress_time = 0;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	sleep_time = config->sleep;
888c2ecf20Sopenharmony_ci	load_time = config->load;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* For the progress bar */
918c2ecf20Sopenharmony_ci	for (_round = 1; _round <= config->rounds; _round++)
928c2ecf20Sopenharmony_ci		total_time += _round * (config->sleep + config->load);
938c2ecf20Sopenharmony_ci	total_time *= 2; /* powersave and performance cycles */
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	for (_round = 0; _round < config->rounds; _round++) {
968c2ecf20Sopenharmony_ci		performance_time = 0LL;
978c2ecf20Sopenharmony_ci		powersave_time = 0LL;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci		show_progress(total_time, progress_time);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci		/* set the cpufreq governor to "performance" which disables
1028c2ecf20Sopenharmony_ci		 * P-State switching. */
1038c2ecf20Sopenharmony_ci		if (set_cpufreq_governor("performance", config->cpu) != 0)
1048c2ecf20Sopenharmony_ci			return;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci		/* calibrate the calculation time. the resulting calculation
1078c2ecf20Sopenharmony_ci		 * _rounds should produce a load which matches the configured
1088c2ecf20Sopenharmony_ci		 * load time */
1098c2ecf20Sopenharmony_ci		calculations = calculate_timespace(load_time, config);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci		if (config->verbose)
1128c2ecf20Sopenharmony_ci			printf("_round %i: doing %u cycles with %u calculations"
1138c2ecf20Sopenharmony_ci			       " for %lius\n", _round + 1, config->cycles,
1148c2ecf20Sopenharmony_ci			       calculations, load_time);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci		fprintf(config->output, "%u %li %li ",
1178c2ecf20Sopenharmony_ci			_round, load_time, sleep_time);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci		if (config->verbose)
1208c2ecf20Sopenharmony_ci			printf("average: %lius, rps:%li\n",
1218c2ecf20Sopenharmony_ci				load_time / calculations,
1228c2ecf20Sopenharmony_ci				1000000 * calculations / load_time);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci		/* do some sleep/load cycles with the performance governor */
1258c2ecf20Sopenharmony_ci		for (cycle = 0; cycle < config->cycles; cycle++) {
1268c2ecf20Sopenharmony_ci			now = get_time();
1278c2ecf20Sopenharmony_ci			usleep(sleep_time);
1288c2ecf20Sopenharmony_ci			ROUNDS(calculations);
1298c2ecf20Sopenharmony_ci			then = get_time();
1308c2ecf20Sopenharmony_ci			performance_time += then - now - sleep_time;
1318c2ecf20Sopenharmony_ci			if (config->verbose)
1328c2ecf20Sopenharmony_ci				printf("performance cycle took %lius, "
1338c2ecf20Sopenharmony_ci					"sleep: %lius, "
1348c2ecf20Sopenharmony_ci					"load: %lius, rounds: %u\n",
1358c2ecf20Sopenharmony_ci					(long)(then - now), sleep_time,
1368c2ecf20Sopenharmony_ci					load_time, calculations);
1378c2ecf20Sopenharmony_ci		}
1388c2ecf20Sopenharmony_ci		fprintf(config->output, "%li ",
1398c2ecf20Sopenharmony_ci			performance_time / config->cycles);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci		progress_time += sleep_time + load_time;
1428c2ecf20Sopenharmony_ci		show_progress(total_time, progress_time);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci		/* set the powersave governor which activates P-State switching
1458c2ecf20Sopenharmony_ci		 * again */
1468c2ecf20Sopenharmony_ci		if (set_cpufreq_governor(config->governor, config->cpu) != 0)
1478c2ecf20Sopenharmony_ci			return;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci		/* again, do some sleep/load cycles with the
1508c2ecf20Sopenharmony_ci		 * powersave governor */
1518c2ecf20Sopenharmony_ci		for (cycle = 0; cycle < config->cycles; cycle++) {
1528c2ecf20Sopenharmony_ci			now = get_time();
1538c2ecf20Sopenharmony_ci			usleep(sleep_time);
1548c2ecf20Sopenharmony_ci			ROUNDS(calculations);
1558c2ecf20Sopenharmony_ci			then = get_time();
1568c2ecf20Sopenharmony_ci			powersave_time += then - now - sleep_time;
1578c2ecf20Sopenharmony_ci			if (config->verbose)
1588c2ecf20Sopenharmony_ci				printf("powersave cycle took %lius, "
1598c2ecf20Sopenharmony_ci					"sleep: %lius, "
1608c2ecf20Sopenharmony_ci					"load: %lius, rounds: %u\n",
1618c2ecf20Sopenharmony_ci					(long)(then - now), sleep_time,
1628c2ecf20Sopenharmony_ci					load_time, calculations);
1638c2ecf20Sopenharmony_ci		}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		progress_time += sleep_time + load_time;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		/* compare the average sleep/load cycles  */
1688c2ecf20Sopenharmony_ci		fprintf(config->output, "%li ",
1698c2ecf20Sopenharmony_ci			powersave_time / config->cycles);
1708c2ecf20Sopenharmony_ci		fprintf(config->output, "%.3f\n",
1718c2ecf20Sopenharmony_ci			performance_time * 100.0 / powersave_time);
1728c2ecf20Sopenharmony_ci		fflush(config->output);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci		if (config->verbose)
1758c2ecf20Sopenharmony_ci			printf("performance is at %.2f%%\n",
1768c2ecf20Sopenharmony_ci				performance_time * 100.0 / powersave_time);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci		sleep_time += config->sleep_step;
1798c2ecf20Sopenharmony_ci		load_time += config->load_step;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci}
182