Shell: Support time -n <iterations>

You can now specify a number of iterations when timing a command.
The default value is 1 and behaves exactly as before.

If the iteration count is greater than 1, the command will be executed
that many times, and then you get a little timing report afterwards with
the average runtime per iteration, and also the average runtime
excluding the very first iteration. (Excluding the first iteration is
useful when it's slowed down by cold caches, etc.)

This is something I've been doing manually forever (running `time foo`
and then eyeballing the results to headmath an average) and this makes
that whole process so much nicer. :^)
This commit is contained in:
Andreas Kling 2021-08-23 18:59:46 +02:00
parent 96909f5200
commit b9ab7a5095
Notes: sideshowbarker 2024-07-19 01:59:31 +09:00

View file

@ -906,27 +906,61 @@ int Shell::builtin_time(int argc, const char** argv)
{
Vector<const char*> args;
int number_of_iterations = 1;
Core::ArgsParser parser;
parser.add_option(number_of_iterations, "Number of iterations", "iterations", 'n', "iterations");
parser.set_stop_on_first_non_option(true);
parser.add_positional_argument(args, "Command to execute with arguments", "command", Core::ArgsParser::Required::Yes);
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
return 1;
if (number_of_iterations < 1)
return 1;
AST::Command command;
for (auto& arg : args)
command.argv.append(arg);
auto commands = expand_aliases({ move(command) });
Core::ElapsedTimer timer;
Vector<int> iteration_times;
int exit_code = 1;
timer.start();
for (auto& job : run_commands(commands)) {
block_on_job(job);
exit_code = job.exit_code();
for (int i = 0; i < number_of_iterations; ++i) {
Core::ElapsedTimer timer;
timer.start();
for (auto& job : run_commands(commands)) {
block_on_job(job);
exit_code = job.exit_code();
}
iteration_times.append(timer.elapsed());
}
warnln("Time: {} ms", timer.elapsed());
if (number_of_iterations == 1) {
warnln("Time: {} ms", iteration_times.first());
} else {
float total_time = 0;
for (auto time : iteration_times)
total_time += static_cast<float>(time);
float average = total_time / iteration_times.size();
float total_time_excluding_first = 0;
for (size_t i = 1; i < iteration_times.size(); ++i)
total_time_excluding_first += static_cast<float>(iteration_times[i]);
float average_excluding_first = total_time_excluding_first / (iteration_times.size() - 1);
warnln("Timing report:");
warnln("==============");
warn("Command: ");
for (auto& string : args)
warn("{} ", string);
warnln("");
warnln("Average time: {} ms", average);
warnln("Excluding first: {} ms", average_excluding_first);
}
return exit_code;
}