我正在根据我的 Perl 脚本的多次迭代中的标准进行大量文件搜索,它似乎占用了 100% 的 CPU 时间。有没有办法控制我的脚本 CPU 利用率?我在某处读到关于在我的脚本中放置空睡眠周期的内容。但我不知道该怎么做。
6 回答
提高 CPU 利用率的一个好方法是使用更好的算法。不要猜测您的代码将所有时间花在哪里:使用分析器。Devel::NYTProf
是一个很棒的工具。
请务必牢记阿姆达尔定律。例如,假设您的程序的一部分使用了二次算法,并且您可以通过一些努力将其替换为线性算法。万岁!但是如果有问题的代码只占总运行时间的 5%,那么你最英勇的努力只能带来 5% 的微小改进。使用分析器确定其他地方是否有更大加速的机会。
你不会告诉我们你在搜索什么,即使是最知名的算法也可能是 CPU 密集型的。考虑到您的操作系统的调度程序已经过编写、手动调整、测试和重写,以有效地使用系统资源。是的,有些任务需要专门的调度程序,但这种情况很少见——考虑到您使用的是 Perl,这种可能性更小。
不要把它当作你的代码正在消耗 CPU 的坏信号。您可能会惊讶地发现,在性能至关重要的实时系统中,最困难的挑战之一就是让 CPU 保持忙碌而不是空闲。
您可以使用sleep 或 usleep。您可以做的另一件事是降低进程优先级。
更新:见setpriority()
睡吧:
while ($not_done_yet) {
do_stuff_here();
sleep 1; # <-- sleep for 1 second.
}
或者稍微花哨一点,每个睡眠周期执行 N 次操作:
my $op_count = 0;
while ($not_done_yet) {
do_stuff_here();
$op_count ++;
if ($op_count >= 100) {
$op_count = 0;
sleep 1; # <-- sleep for 1 second every 100 loops.
}
}
my $base = time;
my $ratio = 0.5;
my $used = 0;
sub relax {
my $now = time;
my ($total) = times;
return if $now - $base < 10 or $total - $used < 5; # otherwise too imprecise
my $over = ($total - $used) - $ratio * ($now - $base);
$base = $now + ($over > 0 && sleep($over));
$used = $total;
}
(未经测试...)relax
在整个代码中撒上足够多的调用,这应该平均接近或低于 50% 的 CPU 时间。
BSD::Resource可以更轻松地做到这一点,您不妨抓住Time::HiRes以获得更高的精度。
my $base = clock_gettime(CLOCK_MONOTONIC);
my (undef, $hard) = getrlimit(RLIMIT_CPU);
my $interval = 10;
if ($hard != RLIM_INFINITY && $hard < $interval) {$interval = $hard / 2}
my $ratio = 0.5;
$SIG{XCPU} = sub {
setrlimit(RLIMIT_CPU, $interval, $hard);
my $now = clock_gettime(CLOCK_MONOTONIC);
my $over = $interval - $ratio * ($now - $base);
$base = $now + ($over > 0 && sleep($over));
};
setrlimit(RLIMIT_CPU, $interval, RLIM_INFINITY);
(也未经测试...)在支持它的系统上,这应该要求操作系统每秒钟向您发出$interval
CPU 时间的信号,此时您重置计数器并休眠。这不需要对其余代码进行任何更改。
您的脚本实际上一直在做事吗?例如,如果你计算一个 mandelbrot 集,你会有一些循环消耗 CPU,但一直在积极地处理数据。
或者您是否有等待更多数据处理的循环:
while(1) {
process_data() if data_ready();
}
在第一种情况下,设置优先级可能是最好的解决方案。它会减慢计算速度,但仅限于为系统上的任何其他进程提供服务所需的速度。
在第二种情况下,您可以通过只休眠几分之一秒来显着提高 CPU 利用率。
while(1) {
process_data() if data_ready();
select( undef, undef, undef, 0.1 );
}
如果您从可以操作的源中提取数据,select
那就更好了。您可以安排循环阻塞,直到数据准备好。
use IO::Select;
my $s = IO::Select->new($handle);
while(1) {
process_data() if $s->can_read;
}
Select 适用于 *NIX 系统上的套接字和文件句柄。在 Windows 系统上,您只能针对套接字进行选择。