summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2021-08-23 18:59:46 +0200
committerAndreas Kling <kling@serenityos.org>2021-08-23 20:15:14 +0200
commitb9ab7a509544ef968e094dac79a0733da9d56095 (patch)
treec4567985fb416e3b7d839a1126cfcdbb387e29a0 /Userland
parent96909f5200f524ce0157f12df1a0c4bba103fe8b (diff)
downloadserenity-b9ab7a509544ef968e094dac79a0733da9d56095.zip
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. :^)
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Shell/Builtin.cpp46
1 files changed, 40 insertions, 6 deletions
diff --git a/Userland/Shell/Builtin.cpp b/Userland/Shell/Builtin.cpp
index bb290e7962..d0e7417769 100644
--- a/Userland/Shell/Builtin.cpp
+++ b/Userland/Shell/Builtin.cpp
@@ -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());
+ }
+
+ 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);
}
- warnln("Time: {} ms", timer.elapsed());
+
return exit_code;
}