Perl スクリプトからの複数回の反復で基準に基づいて多くのファイル検索を行っていますが、CPU 時間が 100% 使用されているようです。スクリプトの CPU 使用率を制御する方法はありますか? スクリプトに空の睡眠サイクルを入れることについてどこかで読みました。しかし、これを行う方法がわかりません。
6 に答える
OS( Windowsまたは Linux)によって割り当てられるプロセス(Perlの)優先度を下げることができます
最低優先度の例:
ウィンドウズ
start /LOW perl <myscript>
Linux
nice +19 perl <myscript>
CPU使用率を改善するための優れた方法は、より優れたアルゴリズムを使用することです。コードが常にどこで費やしているかを推測しないでください。プロファイラーを使用してください。Devel::NYTProf
このための素晴らしいツールです。
アムダールの法則を忘れないでください。たとえば、プログラムの一部が2次アルゴリズムを使用していて、ある程度の努力を払えば、線形アルゴリズムに置き換えることができるとします。やったー!しかし、問題のコードが総実行時間の5%しか占めていない場合、最も英雄的な努力は、わずか5%の改善に勝るものはありません。プロファイラーを使用して、より高速化の機会が他の場所で利用可能かどうかを判断します。
何を検索しているのかを教えてくれません。最もよく知られているアルゴリズムでさえ、CPUを集中的に使用する可能性があります。オペレーティングシステムのスケジューラが、システムリソースを効率的に使用するために作成、手動調整、テスト、および再作成されていることを考慮してください。はい、一部のタスクには専用のスケジューラーが必要ですが、そのようなケースはまれです。Perlを使用している場合はさらに少なくなります。
コードがCPUを使い果たしているという悪い兆候と見なさないでください。パフォーマンスが重要なリアルタイムシステムで最も難しい課題の1つは、アイドリングではなく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
と、平均してCPU時間の50%近くまたはそれ以下になるはずです。
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);
(これもテストされていません...)これをサポートするシステムでは、OSに$interval
CPU時間の秒ごとに信号を送るように要求する必要があります。その時点で、カウンターをリセットしてスリープします。これにより、残りのコードを変更する必要はありません。
あなたのスクリプトは実際にずっと何かをしていますか? たとえば、マンデルブロ集合を計算すると、CPU を消費するループが発生しますが、常にアクティブにデータを処理しています。
または、より多くのデータが処理されるのを待っているループがありますか?
while(1) {
process_data() if data_ready();
}
最初のケースでは、優先度を設定することがおそらく最善の解決策です。計算は遅くなりますが、システム上の他のプロセスにサービスを提供するために必要な分だけです。
2 番目のケースでは、ほんの一瞬スリープするだけで、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 システムでは、ソケットに対してのみ選択できます。