使用 perl 脚本优化使用可用的 cpu

use perl script to optimally use available cpus

我写了一个小的 perl 脚本,它多次启动一个程序,在 for 循环中使用不同的参数。该程序进行数值计算,如果可以得到一个,则使用整个CPU。我有几个 CPU 可用,所以理想情况下,我想一次启动与可用 CPU 一样多的程序实例,但不要更多。由于可能还有其他进程运行,可用CPU的数量并不总是相同的。

到目前为止我所做的是:

#!/usr/bin/perl

use strict;
use warnings;

use IPC::Open2;
use Parallel::ForkManager;

my $program = "./program";

my($out, $in);
my $pid;

my $pm = new Parallel::ForkManager(44);

for my $x (0..100){
          my $childpid = $pm->start and next; 
          $pid= open2($out, $in, $program);

          print $in <<EOF;
          #input involving $x
EOF
          my $printstring = "";
          while(<$out>){
            if (/^\s*1\.000\s+(-\S+)D(\S+)\s*$/){
               $printstring .= "e";
            }
          }
          print $printstring, "\n";
          waitpid( $pid, 0 );
          $pm->finish;

}
$pm->wait_all_children;
print "\n\n END\n";

这显然包含要启动的固定数量的进程,因此可以使用固定数量的 CPUs,我不知道如何改变它以灵活地确定可用的 CPUs 并相应地更改 children 的数量。任何想法如何做到这一点?

更新:

明确一点,这里的限制因素绝对是 CPU 时间而不是 I/O 东西。

我调查了 loadavg,但我对它的输出感到困惑。

68.71 66.40 63.72 70/1106 19247

同时top显示

Tasks: 978 total,  23 running, 955 sleeping,   0 stopped,   0 zombie
Cpu(s):  2.1%us,  1.5%sy, 93.3%ni,  3.1%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st

CPU的个数是48个,所以我会想如果第四个数(这里是70)大于48,我就不应该再启动child个进程了, 但根据 top 似乎有一些空闲 CPU 那里,虽然第四个数字是 70.

可能总会有进程跳来跳去,有些进程会比其他进程使用更多 CPU。我认为另一种方法是查看每个 CPU 使用其空闲百分比的繁忙程度。像下面的代码片段这样的东西可以实现这个目标。然后您可以设置一个阈值来确定它是否超过某个空闲量。然后,您可以使用返回的数字来使您的逻辑基于要启动的进程数。我相信这样的事情会有所帮助:

#!/usr/bin/env perl

use strict;
use warnings;
use FileHandle;

#Get number of cores over 95% idle
# this can be adjusted
my $idle_percent=90;
my $free_cores=GetCores($idle_percent);
printf( "Cores over %s free: %s\n",$idle_percent,$free_cores);

sub GetCores {
    my $threshold=shift;
    my $cpu_idle_count=0;

    my $delta_time_sleep=2; #Amount of sleep between the 2 samples
    my @cpu_idle_totals;
    my @cpu_total_totals;

    for(0..1) {
        my $output_fh=FileHandle->new('/proc/stat','r') or die "No stat";
        # Get output of /proc/stat
        while ( my $line=$output_fh->getline() ) {
            chomp($line);
            my ($tag,$user,$nice,$system,$idle,$iowait,$irq,$softirq)
                =split( /\s+/, $line);

            if ( $tag=~ m/cpu(.+)/ ) {
                my $cpu_number=;

                my $total=( 
                    $user + $nice + $system + $idle 
                    + $iowait + $irq + $softirq
                );

                if ( defined( $cpu_idle_totals[$cpu_number] ) ) {
                    my $idle_delta=$idle-$cpu_idle_totals[$cpu_number];
                    my $total_delta=$total-$cpu_total_totals[$cpu_number];
                    my $usage=100 * (($idle_delta)/$total_delta);
                    printf("%s is %0.2f%% idle\n",$tag,$usage);

                    if ( $usage >= $threshold ) {
                        $cpu_idle_count++;
                    }
                }

                $cpu_idle_totals[$cpu_number]=$idle;
                $cpu_total_totals[$cpu_number]=$total;

            }
        }

        $output_fh->close();
        sleep $delta_time_sleep;
    }


    return $cpu_idle_count;
} 

输出:

cpu0 is 89.90% idle
cpu1 is 94.97% idle
cpu2 is 95.02% idle
cpu3 is 97.00% idle
cpu4 is 96.98% idle
cpu5 is 98.48% idle
cpu6 is 97.99% idle
cpu7 is 95.98% idle
Cores over 90% free:7

我建议采取稍微不同的方法 - 怎么样,而不是 'throttling' 基于负载的活动进程数 - 为什么不使用 SIGSTOPSIGCONT

Parallel::ForkManager 为您提供 running_procs 方法,其中 returns PID 列表。

当平均负载达到 'too high' 时,您可以 signal 将这些 STOP

您可以使用 Sys::Info::CPU (This also tells you load) or - perhaps look at Number of processors/cores in command line

找到 "too high"

但理论上 - 当负载过高时,向您的一些子进程发出 'SIGSTOP'。他们 应该 退出 运行 队列,可见但被暂停。

就平均负载而言 - 您会得到 3 个数字。 1m、5m 和 15m CPU 负载。查看第一个,如果它大于 CPU 的数量,则说明存在争用。