【问题标题】:How to time the execution of a gnu make rule?如何定时执行 gnu make 规则?
【发布时间】:2016-01-21 22:26:43
【问题描述】:
我想为 make 规则的执行计时,但在获取有效的开始和停止时间以及让简单的算术(减法)工作时遇到了麻烦。
$ make money
sleep 2
echo "money: 1453412887" > logfile
echo "money: 1453412887" >> logfile
echo "money: 1453412887" >> logfile
在Makefile:
money:
$(eval start := $(shell date +%s))
sleep 2
$(eval end := $(shell date +%s))
echo "$@: ${start}" > logfile
echo "$@: ${end}" >> logfile
echo "$@: $(shell date +%s)" >> logfile
【问题讨论】:
标签:
makefile
profiling
timing
【解决方案1】:
请尝试以下示例,
test:
@start=$$(date +%s); \
echo $@: $$start > test.log
@sleep 2
@end=$$(date +%s); \
echo $@: $$end >> test.log
,一个关键的项目是,shell变量赋值应该和echo命令在同一行。
【解决方案2】:
如果您只想跟踪一个目标的时间,Eric 的答案是一个不错的答案。虽然,没有理由设置一个单独的变量;只回显命令的输出更简单。此外,如果您对 shell 命令使用老式反引号而不是较新的 $(...) 格式,则在 makefile 配方中可能更容易阅读:
test:
echo $@: `date +%s` > test.log
@sleep 2
echo $@: `date +%s` >> test.log
您的尝试不起作用的原因是 make 将在开始第一行之前评估配方的所有行。。因此,整个配方中的所有 make 变量和函数(如 eval、shell 等)都在配方的任何部分开始之前预先扩展。所以多次运行date 命令的结果总是相同的。
如果您尝试为所有命令计时并且不想更改所有配方以保留日志,则另一种选择是编写一个特殊的脚本或程序,它将命令行作为参数,然后定时在 shell 中运行该命令。然后设置 make SHELL 变量以使用该脚本/程序而不是真正的 shell。如果你有复杂的配方(你必须保留所有的引用等),让它正常工作会有点棘手。实际上编写一个小 C 程序并使用 fork/exec 是最简单的:那里没有引用问题。但是,这是最可靠且侵入性最小的方法。
【解决方案3】:
这是我用来计时执行 makefile 构建项(配方及其各自的项,甚至是项的输出)的解决方案。
make 命令的输出的原理是通过管道传送到这个
while read line; do date +'%s%N'; echo "$line"; done`
它在所有从 make 调用发出的输出行前面加上一个时间戳行,允许查看用于各个配方及其元素/项目的时间。
一个例子说明了这一点。假设这个简单的 Makefile
如果这是我们的 Makefile
all: part1 part2
cat part1 part2 > all
part1:
sleep 1
echo "part1 content" > part1
part2:
sleep 2
echo "part2 content" > part2
然后运行它会导致这个输出:
$> make | while read line; do date +'%s%N'; echo "$line"; done
1561566560276946890
sleep 1
1561566561280995480
echo "part1 content" > part1
1561566561290389273
sleep 2
1561566563292382045
echo "part2 content" > part2
1561566563298681800
cat part1 part2 > all
不出所料表明 part1 配方步骤 sleep 1 的规则采用了 1561566561280995480 - 1561566560276946890 = 1 004 048 640 纳秒,相当于
到预期的 1 秒持续时间。
生成的 unix epoc 纳秒时间戳,如this answer中所建议
提供一种简单的方法来快速检查输出的持续时间和
从而获得好评。
警告。通过-j命令行参数使用多个make作业时,时间戳只能反映-Onone的时间,即不使用输出同步。
【解决方案4】:
这里是如何做到这一点,而无需在 makefile 上乱涂乱画。
基本上,
您将 make 通常用于执行命令 (/bin/sh) 的 shell 替换为您自己设计的 shellscript。
单线就可以了。比如:
$ cat ~/TIMING
#!/bin/bash
command time -ao ~/LOG -f "%E [$*]" bash "$@"
-
command 因为我们想使用 /bin/time (YMMV) 而不是 bash 内置 time。
-
-ao 将消息附加到日志文件中。我们使用绝对路径,以便子使更改文件夹继续记录到文件。
-
-f 指定时间格式:我选择经过时间,后跟 make 提供给 shell 的参数。
-
"$@" 扩展为 make 传递给 shell 的参数。 make 使用的第一个参数应该是-c,这会导致 bash 将以下参数作为命令执行。
- 退出代码是 make 要求执行的命令的退出代码。
测试
$ cat Makefile
.PHONY: atest
atest:
sleep 1.5
echo testing non-zero exit code
exit 22
echo done
然后正常运行:
$ make
sleep 1.5
echo testing non-zero exit code
testing non-zero exit code
exit 22
make: *** [atest] Error 22
然后是定时运行。看起来都一样:
$ rm -f ~/LOG
$ chmod a+x ~/TIMING
$ make SHELL=~/TIMING
sleep 1.5
echo testing non-zero exit code
testing non-zero exit code
exit 22
make: *** [atest] Error 22
但是这次我们有一个不错的日志文件:
$ cat ~/LOG
0:01.50 [-c sleep 1.5]
0:00.00 [-c echo testing non-zero exit code]
0:00.00 [-c exit 22]
很好。并行安全也。你的 makefile 是并行安全的,对吧?