How can I get the CPU time for a perl system call?

2019-02-27 05:41发布

I have a perl script that calls external executables using system(). I would like to measure the CPU seconds taken by these external programs. Ideally, I would like to run them using the shell builtin time command (this is on a Linux system). Something like this:

system("time /path/to/command")

Now, time prints its output to stderr, but in order to do so, launches the command it is given in a separate subshell. This means that in order to capture time's output when running manually in the shell, you need to explicitly use a subshell and redirect the subshell's stderr:

$ time ( command > command.log 2> command.er) 2> time.out

The file time.out will have the output of the time command while command.er has the stderr of command. Unfortunately, the parentheses break perl's system call:

$ time ( ls ) 2> er ## works
$ perl -e 'system("time (ls)")'
sh: 1: Syntax error: word unexpected (expecting ")")

And this means I can't capture the output of time. To make matters wors, this seems to be version dependent:

$ perl --version | head -n2
This is perl 5, version 18, subversion 2 (v5.18.2) built for x86_64-linux-gnu-thread-multi

But if I try the same thing with a newer version:

$ perl --version | head -n2
This is perl 5, version 24, subversion 1 (v5.24.1) built for x86_64-linux-thread-multi
$ perl -e 'system("time (ls)")'
file1

real    0m0.002s
user    0m0.000s
sys     0m0.000s

Unfortunately, I need this to run on a production machine so upgrading Perl is not an option. So, how can I time a system call in Perl 5.18? I need the user and sys values, so simply recording the start and end times won't help. I am willing to use a dedicated module if that's necessary although I would prefer a trick that lets me use the shell's time.


UPDATE: it turns out the difference in behavior is not because of the newer perl version but, instead, it is because I tested it on an Arch system whose /bin/sh is bash while the other commands were being run on Ubuntu systems whose /bin/sh is dash, a minimal shell that doesn't support parentheses for subshells.

3条回答
迷人小祖宗
2楼-- · 2019-02-27 06:08

You've tested the command with bash, but you passed it to sh.

system("time (ls)")

is short for

system("/bin/sh", "-c", "time (ls)")

but you want

system("/bin/bash", "-c", "time (ls)")
查看更多
我只想做你的唯一
3楼-- · 2019-02-27 06:09
$ time ( ls ) 2> er ## works
$ perl -e 'system("time (ls)")'
sh: 1: Syntax error: word unexpected (expecting ")")

The problem is that in first case, your shell is probably /bin/bash whereas in second case it is /bin/sh. If you want to run your command with another shell, you could use system LIST form:

system("/bin/bash", "-c", "time(ls)")

Note 1: There's PERL5SHELL environmnet value, but that seems to take effect only on Win32.

Note 2: If you want to measure CPU time of child process, you could use Unix::Getrusage or BSD::Resource modules.

查看更多
走好不送
4楼-- · 2019-02-27 06:24

You can use Capture::Tiny to capture the STDOUT and STDERR of pretty much anything in Perl.

use Capture::Tiny 'capture';

my ($stdout, $stderr, $exit) = capture { system "time ls" };

print $stderr;

For some reason the output is missing some whitespace on my system, but is clear enough to parse out what you need.

0.00user 0.00system 0:00.00elapsed 0%CPU (0avgtext+0avgdata 2272maxresident)k
0inputs+8outputs (0major+111minor)pagefaults 0swaps
查看更多
登录 后发表回答