1

更新:コードのデルタ計算を修正しましたが、まだ問題が残っています

皆さん、次のコードを使用して、私が時々非常に奇妙な結果を得る理由を説明していただけますか?

#include <unistd.h>
#include <sys/time.h>
#include <stdio.h>

int main()
{
  struct timeval start, end;
  long mtime1, mtime2, diff;

  while(1)
  {
    gettimeofday(&start, NULL);

    usleep(2000);

    gettimeofday(&end, NULL);

    mtime1 = (start.tv_sec * 1000 + start.tv_usec/1000.0);
    mtime2 = (end.tv_sec * 1000 + end.tv_usec/1000.0);

    diff = mtime2 - mtime1;
    if(diff > 10) 
        printf("WTF: %ld\n", diff);
  }

  return 0;
}

(次のコマンドでコンパイルして実行できます:gcc test.c -o out -lrt && ./out

私が経験しているのは、ほぼ毎秒、またはさらに頻繁に、散発的に大きなdiff変数の値です。

$ gcc test.c -o out -lrt && ./out 
WTF: 14
WTF: 11
WTF: 11
WTF: 11
WTF: 14
WTF: 13
WTF: 13
WTF: 11
WTF: 16

どうすればこれが可能ですか?責任があるのはOSですか?コンテキストの切り替えが多すぎませんか?しかし、私のボックスはアイドル状態です(負荷平均:0.02、0.02、0.3)。

これが私のLinuxカーネルバージョンです:

$ uname -a
Linux kurluka 2.6.31-21-generic #59-Ubuntu SMP Wed Mar 24 07:28:56 UTC 2010 i686 GNU/Linux
4

5 に答える 5

4

睡眠機能は、あなたが少なくとも一定時間眠ることを確認するだけです。LinuxはリアルタイムOSではないため、必要な時間だけスリープするかどうかはわかりません。その値を当てにすることはできないので、それは問題です。ご指摘のとおり、睡眠時間は本当に長いです。

Linuxスケジューラーはそれを保証できません。リアルタイムOSを使用すると、それを取得できます。

あなたの公式はある意味で間違っていますが、それがあなたがこんなに長い睡眠時間を持っている理由ではないと思います。このスニペットで2つの数式を確認すると、同じ結果が得られます。

#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <stdio.h>

int main()
{
  struct timeval start, end;
  long mtime, mtime2, start_time, end_time, seconds, useconds;

  while(1)
  {
    gettimeofday(&start, NULL);

    usleep(2000);

    gettimeofday(&end, NULL);

    seconds  = end.tv_sec  - start.tv_sec;
    useconds = end.tv_usec - start.tv_usec;

    mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;

    start_time = ((start.tv_sec) * 1000 + start.tv_usec/1000.0) + 0.5;
    end_time = ((end.tv_sec) * 1000 + end.tv_usec/1000.0) + 0.5;

    mtime2 = end_time - start_time;

    if(mtime > 10 || mtime2 > 10)
    {
      printf("WTF: %ld\n", mtime);
      printf("WTF2: %ld\n", mtime2);
    }
  }

  return 0;
}

結果 :

$ gcc test.c -o out -lrt && ./out
WTF: 11
WTF2: 12
WTF: 21
WTF2: 21

usecondsの部分は周期的であり、大きなマイナスの違いにつながる可能性があるため、間違っていると思いました。しかし、符号付き長整数を使用するので、それほど大きな時間にはなりません...

my2cents

編集:男からnanosleep:

nanosleep()の現在の実装は、1 / HZの解像度を持つ通常のカーネルタイマーメカニズムに基づいています(time(7)を参照)。したがって、nanosleep()は常に少なくとも指定された時間一時停止しますが、プロセスが再び実行可能になるまで、指定された時間より最大10ミリ秒長くかかる場合があります。同じ理由で、* remで配信された信号の場合に返される値は、通常、1/HZの次に大きい倍数に丸められます。

于 2010-05-21T13:28:57.400 に答える
2

私の推測では、それはOSに関連していると思います。プロセスをリアルタイムの優先順位で実行してみて(chrtプログラムを参照)、それが役立つかどうかを確認してください。

別の注意点として、mtimeを誤って計算しています。これが私が使用するルーチンですが、これはstructtimevalではなくstructtimespec用です(マイクロ秒ではなくナノ秒)。原則は明確である必要があります。

timespec diff(timespec start, timespec end)
{
    timespec temp;
    if ((end.tv_nsec - start.tv_nsec) < 0) {
        temp.tv_sec = end.tv_sec - start.tv_sec - 1;
        temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
    } else {
        temp.tv_sec = end.tv_sec - start.tv_sec;
        temp.tv_nsec = end.tv_nsec - start.tv_nsec;
    }
    return temp;
}
于 2010-05-21T13:14:51.800 に答える
1

見つかりました。manページを参照してください

http://linux.die.net/man/3/usleep

システムタイマーの粒度による。

これは10ミリ秒のafaikです。そのためusleep、プロセスが再スケジュールされるずっと前に、が期限切れになる可能性があります。

また、「通常の」タイムスライスの大きさのオーダーで取得する値とも一致します。

于 2010-05-21T13:56:03.513 に答える
0

あなたの公式は間違っています。同じスケールで両方の時間を変換する必要があります。あなたの例ではms。

double mtime1 = (start.tv_sec * 1000 + start.tv_usec/1000.0) ;
double mtime2 = (end.tv_sec * 1000 + end.tv_usec/1000.0) ;

double diff = mtime2 - mtime1;
if(diff > 10) 
  printf("WTF: %ld\n", diff);

修正された値を差し引く必要があります

例: t1 = 1.999999 t2 = 2.0000012 µsの間隔

あなたの公式であなたは計算します:

2 - 1 == 1 そして、明らかに誤りである==0.5021 - 9999999の結果を与え ます。(1 * 1000 - 999998 / 1000) + 0.5

私の方法は次のようになります。

mtime1 = (1 * 1000 + 999999 / 1000) = 1999.999
mtime2 = (2 * 1000 +      1 / 1000) = 2000.001

2000.001 - 1999.999 = 0.002 ms
于 2010-05-21T13:12:22.470 に答える
0

私はすでにそのような措置を講じていますが、私の結論はまったく同じです。WindowsとLinuxで同じように起こります。

10 ^ -n秒でヒストグラムを作成するプログラムは、次の結果をもたらします。

0.1 0
0.01 0
0.001 0
0.0001 2
1e-05 24
1e-06 69726023
Total: 69726049
Duration: 6.47403 seconds.
Average: 0.0928495 microseconds.

ただし、これはまったく新しいシステムであることに注意してください。これを1年前に2004年のシステムで使用し、0.01の範囲(10ミリ秒以上)で1秒あたり数回ヒットしたことを思い出します。

于 2010-05-21T13:13:27.327 に答える