CPU使用率は高いが、平均負荷は低い


28

CPU使用率は高いが負荷平均はかなり低いという奇妙な動作に陥っています。

この動作は、監視システムの次のグラフで最もよく説明されています。

CPU使用率と負荷

11:57頃に、CPU使用率は25%から75%になります。負荷平均は大幅に変更されません。

それぞれ2つのハイパースレッドを持つ12コアのサーバーを実行します。OSはこれを24個のCPUと見なします。

CPU使用率データは、/usr/bin/mpstat 60 1毎分実行することにより収集されます。all行と列のデータは%usr上のチャートに示されています。これは、「スタック」使用率ではなく、CPUあたりの平均データを示していると確信しています。グラフで75%の使用率が表示されていますが、プロセスでは約2000%の「スタック」CPUを使用していることがわかりtopます。

負荷平均値は、/proc/loadavg毎分から取得されます。

uname -a 与える:

Linux ab04 2.6.32-279.el6.x86_64 #1 SMP Wed Jun 13 18:24:36 EDT 2012 x86_64 x86_64 x86_64 GNU/Linux

Linux distは Red Hat Enterprise Linux Server release 6.3 (Santiago)

マシンにかなり高い負荷がかかっている状態で、2台のJava Webアプリケーションを実行します。マシンあたり100リクエスト/秒と考えてください。

CPU使用率データを正しく解釈すると、CPU使用率が75%の場合、CPUが平均75%の時間でプロセスを実行していることになります。ただし、CPUが75%の時間でビジーである場合、より高い負荷平均は見られませんか?実行キューに2〜4個のジョブしかないのに、CPUが75%ビジーになる可能性はありますか?

データを正しく解釈していますか?この動作の原因は何ですか?


監視システムは正規化されたCPU負荷(負荷/ #CPU)を示していますか?通常のLinux CPU負荷は、異なるコア/ CPUカウントのシステム間で比較するのが難しいため、一部のツールは代わりに正規化されたCPU負荷を使用します。
ブライアン

各データポイントをCPUの数で割るということですか?すなわち、私たちの場合、loadavg / 24?役立つ場合は、データからこのようなグラフを簡単に作成できます。
Kアーランソン

私はあなたのチャートがすでにそれを示しているかもしれないと提案していました。
ブライアン

ああ、誤解して申し訳ありません。これは良い説明でしたが、残念ながら、システム全体の負荷平均が表示されます。トリプルチェックしました。
Kアーランソン

回答:


51

少なくともLinuxでは、負荷平均とCPU使用率は実際には2つの異なるものです。負荷平均は、一定期間にカーネル実行キューで待機しているタスクの数(CPU時間だけでなく、ディスクアクティビティ)の測定値です。CPU使用率は、CPUが現在どれだけビジーであるかの尺度です。1分間100%で固定された1つのCPUスレッドが1分間の負荷平均に「寄与する」最大負荷は1です。ハイパースレッド(4つの仮想コア)を備えた4コアCPU 1分間の平均負荷。

多くの場合、これら2つの数値には相互に関連するパターンがありますが、同じものと考えることはできません。CPU使用率がほぼ0%の高負荷(待機状態で大量のIOデータがスタックしている場合など)があり、シングルスレッドプロセスを実行している場合は1から100%CPUの負荷をかけることができます。フルチルト。また、短時間でCPUを100%近くで見ることができますが、平均メトリックがまだ「追いついていない」ため、負荷はまだ1未満です。

サーバーの負荷が15,000を超え(実際には誤字ではありません)、CPU%が0%に近いことがわかりました。これは、Samba共有で問題が発生し、多数のクライアントがIO待機状態でスタックし始めたために発生しました。対応するCPUアクティビティのない通常の高負荷数が表示されている場合は、何らかのストレージの問題が発生している可能性があります。仮想マシンでは、これは同じVMホスト上でストレージリソースを奪い合う他のVMがあることも意味します。

また、高負荷は必ずしも悪いことではありません。ほとんどの場合、システムが最大限の容量まで使用されていることを意味します(負荷の数がプロセッサコアの数よりも大きい場合)。私がシステム管理者だった場所には、プライマリシステムの負荷平均をNagiosよりも近くで見ている人がいました。負荷が高いとき、彼らはあなたがSMTPと言うことができるよりも速く24時間365日私に電話するでしょう。ほとんどの場合、実際には何も問題はありませんでしたが、彼らは負荷値を何かが間違っていると関連付け、それを鷹のように見ました。確認した後、私の応答は通常、システムがまさにその仕事をしているというものでした。もちろん、これは負荷が15000を超えた場所と同じ場所でした(ただし、同じサーバーではありません)。システムの目的を考慮する必要があります。それが主力である場合は、負荷が自然に高くなることを期待してください。


1つのスレッドプロセスで1から100%CPUの負荷をかけることができるということはどういう意味ですか?どのようなスレッドについて話しているのですか?Javaプロセスを検討すると、スレッドが大量にありますが、スレッドはOSの観点からはプロセスとして扱われると仮定されていました(結局Linuxには個別のPIDがあります)。単一のマルチスレッドJavaプロセスが、負荷平均の観点から1つのタスクとしてのみカウントされるようにすることはできますか?
Kアーランソン

私は自分でテストを行いましたが、Javaプロセスのスレッドは、あたかもそれらが別々のプロセスであるかのように負荷平均に貢献します(つまり、ビジー待機ループで10スレッドを実行するJavaクラスは10に近い負荷を与えます)。上記のスレッド化されたプロセスについての説明をお願いします。ありがとうございました!
Kアーランソン

つまり、非マルチスレッドプロセス(つまり、一度に1つのCPUのみを使用するプロセス)がある場合です。たとえば、ビジーループを実行する単純なCプログラムを記述する場合、その単一スレッドが実行され、一度に1つのCPUのみを使用します。
deltaray

私が見つけたすべての情報は、スレッドがカーネルから見たときと負荷を計算するとき、別々のプロセスとして数えると言います。したがって、フルティルトでマルチスレッドプロセスを実行して、マルチCPUシステムで1つの負荷と100%のCPUを実現する方法を理解できません。どういう意味か理解してもらえますか?
Kアーランソン

詳細をお探しの方へ:Brendan Greggによる「Linux Load Averages:Solving the Mystery」には、私がこれまで必要としていたすべての答えがありました。
ニッコリー

24

負荷は非常に不正な数です。塩の粒でそれを取る。

非常に速く連続して多くのタスクを大量に生成する場合、実行キュー内のプロセスの数は、それらの負荷を登録するには小さすぎます(カーネルは5秒ごとに負荷をカウントします)。

この例を考えてみましょう。8個の論理コアを持つホストで、このPythonスクリプトは大きなCPU使用率(約85%)を登録しますが、負荷はほとんどありません。

import os, sys

while True:
  for j in range(8):
    parent = os.fork()
    if not parent:
      n = 0
      for i in range(10000):
        n += 1
      sys.exit(0)
  for j in range(8):
    os.wait()

別の実装、これwaitは8のグループで回避します(これはテストをゆがめるでしょう)。ここでは、親は常に、アクティブなCPUの数で子の数を維持しようとします。そのため、最初の方法よりもずっと忙しくなり、できればより正確になります。

/* Compile with flags -O0 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <err.h>
#include <errno.h>

#include <sys/signal.h>
#include <sys/types.h>
#include <sys/wait.h>

#define ITERATIONS 50000

int maxchild = 0;
volatile int numspawned = 0;

void childhandle(
    int signal)
{
  int stat;
  /* Handle all exited children, until none are left to handle */
  while (waitpid(-1, &stat, WNOHANG) > 0) {
    numspawned--;
  }
}

/* Stupid task for our children to do */
void do_task(
    void)
{
  int i,j;
  for (i=0; i < ITERATIONS; i++)
    j++;
  exit(0);
}

int main() {
  pid_t pid;

  struct sigaction act;
  sigset_t sigs, old;

  maxchild = sysconf(_SC_NPROCESSORS_ONLN);

  /* Setup child handler */
  memset(&act, 0, sizeof(act));
  act.sa_handler = childhandle;
  if (sigaction(SIGCHLD, &act, NULL) < 0)
    err(EXIT_FAILURE, "sigaction");

  /* Defer the sigchild signal */
  sigemptyset(&sigs);
  sigaddset(&sigs, SIGCHLD);
  if (sigprocmask(SIG_BLOCK, &sigs, &old) < 0)
    err(EXIT_FAILURE, "sigprocmask");

  /* Create processes, where our maxchild value is not met */
  while (1) {
    while (numspawned < maxchild) {
      pid = fork();
      if (pid < 0)
        err(EXIT_FAILURE, "fork");

      else if (pid == 0) /* child process */
        do_task();
      else               /* parent */
        numspawned++;
    }
    /* Atomically unblocks signal, handler then picks it up, reblocks on finish */
    if (sigsuspend(&old) < 0 && errno != EINTR)
      err(EXIT_FAILURE, "sigsuspend");
  }
}

この動作の理由は、アルゴリズムが実際のタスク(10000までカウント)を実行するよりも多くの時間を子プロセスの作成に費やしているためです。まだ作成されていないタスクは「実行可能」状態にカウントできませんが、生成されるとCPU時間で%sysを占有します。

そのため、実際に行われている作業が、多数のタスク(スレッド、またはプロセス)を多数連続して生成するという答えが返される可能性があります。


ご提案ありがとうございます。私の質問のグラフは、%ユーザー時間を示しています(CPUシステム時間は除外されています。システム時間はごくわずかしか増加していません)。とにかく多くの小さなタスクが説明になりますか?負荷平均が5秒ごとにサンプリングされる場合、mpstatで指定されたCPU使用率データはより頻繁にサンプリングされますか?
Kアーランソン

私はそこでCPUサンプリングがどのように行われるかについてよく知りません。それに関するカーネルソースを決して読んでいない。私の例では、%usrは70%+で、%sysは15%でした。
マシューイフェ

良い例!
ザビエルルーカス

5

負荷平均があまり増加しない場合は、ハードウェアの仕様と処理されるタスクの性質により、全体的なスループットが良好になり、しばらくタスクキューに蓄積されることを回避することを意味します。

たとえば、平均タスクの複雑さが高すぎるか、タスク平均処理時間がCPUサイクルにかかりすぎるために競合現象が発生した場合、はい、負荷平均は増加します。

更新:

私の元の答えでは明確ではないかもしれないので、今明確にしています:

負荷平均計算の正確な公式は以下の通りloadvg = tasks running + tasks waiting (for cores) + tasks blockedです。

確実に良好なスループットを実現し、負荷平均24に近づけることができますが、タスクの処理時間に対するペナルティはありません。一方、2〜4個の定期的なタスクで十分な速さで完了できない場合、待機中のタスク(CPUサイクル)の数が増え、最終的には高い負荷平均に達することがあります。発生する可能性のあるもう1つのことは、タスクが未処理の同期I / O操作を実行してからコアをブロックし、スループットを低下させ、待機中のタスクキューを大きくすることです(その場合、iowaitメトリックが変化することがあります)


負荷平均には現在実行中のタスクも含まれることを理解しています。つまり、CPUの実際の競合なしに、負荷平均を確実に増やすことができます。それとも私はあなたを誤解/誤解していますか?
Kアーランソン

@KristofferEあなたは完全に正しいです。実際の式は、loadavg =実行中のタスク+待機中のタスク(使用可能なコア用)+ブロックされたタスクです。これは、平均24の負荷、待機またはブロックされたタスクがないことを意味します。したがって、「フル使用」または競合のないハードウェア容量のみがあります。負荷平均と実行中のプロセス数とCPU使用率について混乱しているように思えたので、主に、実行中のプロセス全体が非常に少ない場合でも負荷平均がどのように増加するかについて説明しました。それを再読した後、それは確かにそれほど明確ではないかもしれません。
ザビエルルーカス

2

負荷平均には、ディスクIOでブロックされたタスクが含まれるため、10個のタスクがすべて非常に遅いディスクから読み取ろうとするだけで、CPU使用率がゼロで、負荷平均が10になります。したがって、ビジーなサーバーがディスクのスラッシングを開始することはよくあり、すべてのシークは多くのブロックされたタスクを引き起こし、負荷平均を高めますが、すべてのタスクはディスク上でブロックされるため、CPU使用率は低下します。


1

Matthew Ifeの答えは非常に役に立ち、正しい方向に私たちを導いたが、それは私たちの場合の行動の原因ではなかった。この場合、スレッドプーリングを使用するマルチスレッドJavaアプリケーションがあり、実際のタスクを作成する作業が行われないのはなぜですか。

ただし、スレッドが実際に行う作業は短命であり、IO待機または同期ホーン待機が含まれます。Matthewが彼の答えで言及しているように、負荷平均はOSによってサンプリングされるため、短時間のタスクは見逃される可能性があります。

動作を再現するJavaプログラムを作成しました。次のJavaクラスは、サーバーの1つで28%(650%スタック)のCPU使用率を生成します。これを行っている間、負荷平均は約1.3です。ここで重要なのは、スレッド内のsleep()です。これがないと、負荷の計算が正確になります。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MultiThreadLoad {

    private ThreadPoolExecutor e = new ThreadPoolExecutor(200, 200, 0l, TimeUnit.SECONDS,
            new ArrayBlockingQueue<Runnable>(1000), new ThreadPoolExecutor.CallerRunsPolicy());

    public void load() {
        while (true) {
            e.execute(new Runnable() {

                @Override
                public void run() {
                    sleep100Ms();
                    for (long i = 0; i < 5000000l; i++)
                        ;
                }

                private void sleep100Ms() {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        }
    }

    public static void main(String[] args) {
        new MultiThreadLoad().load();
    }

}

要約すると、理論では、アプリケーションのスレッドが多くアイドル状態になり、その後短時間の作業を実行するため、負荷平均計算によってタスクが正しくサンプリングされないのです。


0

負荷平均は、CPUキュー内のプロセスの平均数です。各システムに固有であり、1つのLAがすべてのシステムで一般的に高く、別のLAが低いとは言えません。したがって、12個のコアがあり、LAを大幅に増やすには、プロセスの数を本当に増やす必要があります。

もう1つの質問は、「CPU使用率」グラフの意味です。あるべきようにSNMPから取得し、SNMPの実装がnet-snmpである場合、12個のCPUのそれぞれからのCPU負荷だけをスタックします。したがってnet-snmp、CPU負荷の合計量は1200%です。

私の仮定が正しい場合、CPU使用率は大幅に増加していません。したがって、LAは大幅に増加しませんでした。


CPUの使用量は、all行であるmpstatから取得されます。すべてのCPUの平均値であり、積み上げられていないことは確かです。たとえば、問題が発生すると、上部には1つのプロセスの2000%のCPU使用率が表示されます。それは積み重ねられた使用法です。
Kアーランソン

0

ここでのシナリオは、少し珍しいものですが、特に予想外のものではありません。Xavierが触れているが、あまり開発されていないのは、Linux(デフォルト)およびほとんどのUnixがプリエンプティブマルチタスクを健全なマシンで実装しているにもかかわらず、タスクがプリエンプションされることはめったにないということです。各タスクにはCPUを占有するためのタイムスライスが割り当てられ、この時間を超え、実行を待機している他のタスクがある場合にのみ横取りされます(負荷はCPUと実行を待機しているプロセスの平均数を報告することに注意してください) 。ほとんどの場合、プロセスは中断されるのではなく、譲歩します。

(一般に、CPUの数に近づいたとき、つまりスケジューラがタスクの横取りを開始したときのみ、負荷について心配する必要があります)。

CPUが75%の時間ビジーである場合、より高い平均負荷を確認する必要はありませんか?

アクティビティのパターンに関するすべてであり、いくつかのタスク(おそらく小さな少数派)によるCPU使用率の増加は、他のタスクの処理に悪影響を与えていませんでした。処理中のトランザクションを分離できれば、減速中に新しいグループが出現し、既存のタスクセットは影響を受けないことが予想されます。

更新

負荷が大幅に増加することなくCPUが高くなる一般的なシナリオの1つは、タスクが他のタスクの1つ(またはシーケンス)をトリガーする場合です。たとえば、ネットワーク要求の受信時にその後、他のプロセスへの非同期呼び出しを行います.... runqueueのサンプリングにより、負荷は実際よりも低く報告されます-しかし、CPU使用率に比例して増加しません-トリガーされたタスクのチェーンは、最初のイベント、およびそれらが(多かれ少なかれ)順番に発生するため、実行キューは膨張しません。


OPは元々、総CPU%が「2000%」であることを示していましたが、これは1つのビジープロセスではなく、多くのタスクがCPUを使用していることを示唆しています。1分間の一貫性が2000%だった場合、通常は負荷が20になると予想します。
マシューイフェ

...質問ではなく、コメントで、そして彼はそれについてあまり確信がありません。「ALL」オプションがない場合、mpstatは平均ではなく合計使用率を報告します。しかし、それは答えを変えません-それは活動のパターンについてです。
symcbean

グラフに表示されているCPU使用率が「CPUごとの平均」であることは100%肯定的です。MpstatはALLなしで実行されますが、CPUごとの情報のみが除外され、all行にはCPUごとの平均が表示されます。質問を明確にします。
Kアーランソン

前のセクションを少し詳しく説明していただけますか?私があなたの言っていることを理解することはできませんが、あなたが引用した私の質問の部分は、私が最も理解するのが難しい部分です。
Kアーランソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.