15

Linux でタイマーのジッターを特徴付けようとしています。私のタスクは、100 ミリ秒のタイマーを実行し、数値がどのように機能するかを確認することでした。

マルチコアマシンで作業しています。標準のユーザー プログラムを setitimer() で使用し、root と同じように実行し、次にプロセッサ アフィニティを使用し、最後にプロセッサ アフィニティとプロセス優先度を使用しました。次に、PREEMPT_RT カーネルで同じことを実行し、PREEMPT_RT ページのデモ コードのように clock_nanosleep() を使用してサンプルを実行しました。すべての実行の中で、タイマーのパフォーマンスは非常に類似しており、変更にもかかわらず実質的な違いはありませんでした。

私たちの最終目標は安定したタイマーです。私が定期的に得ることができる最高の最悪のケースは約200usでした. すべてのケースのヒストグラムは、非常に奇妙な動作を示しています。1 つには、タイマーが早く起動するとは思わないでしょう。しかし、彼らはそうします。ヒストグラムでわかるように、オフセット 0 の両側に谷があります。これらは、2 番目のグラフの 3 つのバンドに表示されます。最初のグラフでは、X 軸はマイクロ秒単位です。2 番目のグラフでは、Y 軸はマイクロ秒単位です。

30 秒のテスト (つまり、300 個のタイマー イベント) を 100 回実行して、いくつかの数値を生成しました。次の図でそれらを確認できます。200us で大きな低下があります。2 番目のグラフには、30000 個のタイマー イベント クロック オフセットがすべてグラフ化されており、いくつかの異常値を確認できます。

X 軸はマイクロ秒単位ですY 軸はマイクロ秒単位です

そこで質問なのですが、この種の分析を以前に行った人はいますか? 同じような行動を見たことがありますか?私の推測では、RT カーネルは負荷の高いシステムで役立つと思いますが、私たちの場合、タイマーのジッターを解消するのには役立ちませんでした。それはあなたの経験ですか?

これがコードです。前に言ったように、PREEMPT_RT サイトで clock_nanosleep() 関数を使用するサンプル コードを変更したので、そのための最小限の変更は含めません。

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <stdlib.h>

#define US_PER_SEC 1000000
#define WAIT_TIME 100000
#define MAX_COUNTER 300

int counter = 0;
long long last_time = 0;
static long long times[MAX_COUNTER];
int i = 0;

struct sigaction sa;

void timer_handler(int signum)
{
    if (counter > MAX_COUNTER)
    {
        sigaction(SIGALRM, &sa, NULL);
        for (i = 0; i < MAX_COUNTER; i++)
        {
            printf("%ld\n", times[i]);
        }
        exit(EXIT_SUCCESS);
    }

    struct timeval t;
    gettimeofday(&t, NULL);

    long long elapsed = (t.tv_sec * US_PER_SEC + t.tv_usec);

    if (last_time != 0)
    {
        times[counter] = elapsed - last_time;
        ++counter;
    }

    last_time = elapsed; 
}

int main()
{
    struct itimerval timer;

    memset(&sa, 0, sizeof(sa));

    sa.sa_handler = &timer_handler;

    sigaction(SIGALRM, &sa, NULL);

    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = 1;

    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = WAIT_TIME;

    setitimer(ITIMER_REAL, &timer, NULL);

    while (1)
    {
        sleep(1);
    }
}

編集: これは Xeon E31220L で、2.2 GHz で実行され、x86_64 Fedora Core 19 が実行されています。

4

2 に答える 2

2

タイマーが早期に起動することを期待しないのは正しいです - そしてそうではありません。明らかに早い発火は、前回のタイマーが期限切れになってからの時間を測定していないためです。つまり、前回のgettimeofday()呼び出しからの時間を測定しています。タイマーが切れてからプロセスが実際にスケジュールされるまでに遅延があった場合、これが遅れてgettimeofday()実行され、次の時間が同じ量だけ早く実行されることがわかります。

後続の呼び出しの差をログに記録する代わりに、gettimeofday()返された絶対時間をログに記録してから、返された時間を最初の時間から N * 100ms と比較してください。

あなたを支援したい場合は、ルートを必要とするテスト プログラム (または)PREEMPT_RTのリアルタイム スケジューラ ポリシーを設定する必要があります。SCHED_FIFOSCHED_RR

于 2013-11-24T23:24:45.393 に答える