16

2 つの通話を並べて、測定可能な最小の継続時間を決定すると、次のようになります。

// g++ -std=c++11 -O3 -Wall test.cpp
#include <chrono>
typedef std::chrono::high_resolution_clock hrc;

hrc::time_point start = hrc::now();
hrc::time_point end   = hrc::now();
std::chrono::nanoseconds duration = end - start;
std::cout << "duration: " << duration.count() << " ns" << std::endl;

これをループで何千回も実行しましたが、特定の 3.40GHz デスクトップで一貫して 40 ns +/- 2 ns を得ています。

しかし、私が眠れる最短時間はどれくらいかを調べてみると、次のようになります。

#include <thread>

hrc::time_point start = hrc::now();
std::this_thread::sleep_for( std::chrono::nanoseconds(1) );
hrc::time_point end   = hrc::now();
std::chrono::nanoseconds duration = end - start;
std::cout << "slept for: " << duration.count() << " ns" << std::endl;

これは、私が平均して 55400 ナノ秒、つまり 55.4 マイクロ秒眠ったことを示しています。思っていたよりもずっと長い時間。

上記のコードをfor()ループに入れて、さまざまな量でスリープしてみました。結果は次のとおりです。

  • sleep_for( 4000 ns ) => 58000 ns スリープ状態
  • sleep_for( 3000 ns ) => 57000 ns スリープ
  • sleep_for( 2000 ns ) => 56000 ns スリープ
  • sleep_for( 1000 ns ) => 55000 ns スリープ
  • sleep_for( 0 ns ) => 54000 ns スリープ
  • sleep_for( -1000 ns ) => 313 ns スリープ
  • sleep_for( -2000 ns ) => 203 ns スリープ
  • sleep_for( -3000 ns ) => 215 ns スリープ
  • sleep_for( -4000 ns ) => 221 ns スリープ

私が持っているいくつかの質問:

  • これらの数字を説明できるものは何ですか?
  • 負の時間スリープすると 200+ ns が返されるのに、0+ ナノ秒でスリープすると 50,000+ ナノ秒になるのはなぜですか?
  • スリープ時間としての負の数は文書化/サポートされている機能ですか、それとも信頼できない奇妙なバグに偶然出くわしたのですか?
  • より一貫した/予測可能なスリープ時間を提供する、より優れた C++ スリープ コールはありますか?
4

3 に答える 3

15

これらの数字を説明できるものは何ですか?

非常に明白なパターンがあります。すべての結果は、スリープを要求する時間よりも一貫して 54000ns 長くなります。this_thread::sleep_for()GCCが GNU/Linuxでどのように実装されているかを見るnanospleepと、Cubbi のコメントにあるように、その関数を呼び出すだけで約 50000ns かかることがわかります。そのコストの一部はシステム コールを行っているため、ユーザー空間からカーネルに切り替えたり戻ったりしていると思います。

負の時間スリープすると 200+ ns が返されるのに、0+ ナノ秒でスリープすると 50,000+ ナノ秒になるのはなぜですか?

推測では、C ライブラリは負の数をチェックし、システム コールを作成しないと言えます。

スリープ時間としての負の数は、文書化/サポートされている機能ですか?それとも、信頼できない奇妙なバグに偶然出くわしたのですか?

標準では、負の引数を渡すことは禁止されていないため、許容されます。相対タイムアウトで指定された時間が既に経過しているため、関数は「すぐに」戻る必要があります。ただし、負でない引数よりも速く返される負の引数に頼ることはできません。これは、特定の実装のアーティファクトです。

より一貫した/予測可能なスリープ時間を提供する、より優れた C++ スリープ コールはありますか?

私はそうは思いません-私が知っていれば、GCCでそれを使用して実装しthis_thread::sleep_for()ます.

編集: GCC の libstdc++ の最近のバージョンでは、以下を追加しました:

if (__rtime <= __rtime.zero())
  return;

そのため、ゼロまたは負の期間が要求された場合、システム コールはありません。

于 2013-08-06T16:59:47.010 に答える
5

Straight Fastの回答に触発されて、timer_slack_nsとの効果を評価しましたSCHED_FIFOtimer_slack_ns追加する必要があるため

#include <sys/prctl.h> // prctl
⋮
prctl (PR_SET_TIMERSLACK, 10000U, 0, 0, 0);

つまり、現在のプロセスでは、タイマー スラックがデフォルト値の 50µs ではなく 10µs に設定されます。その効果は、わずかに高いエネルギー消費を犠牲にして、応答性が向上することです。このプロセスは、権限のないユーザーが引き続き実行できます。スケジューラ ポリシーを変更するには、SCHED_FIDO「root」である必要があります。必要なコードは

#include <unistd.h>    // getpid
#include <sched.h>     // sched_setscheduler
⋮
    const pid_t pid {getpid ()};
    struct sched_param sp = {.sched_priority = 90};
    if (sched_setscheduler (pid, SCHED_FIFO, &sp) == -1) {
        perror ("sched_setscheduler");
        return 1;
    }

GUI を備えたデスクトップ システム (Debian 9.11、カーネル 4.9.189-3+deb9u2、g++ 9.2 -O3、Intel® Core™ i5-3470T CPU @ 2.90GHz) でStéphaneのコード スニペットを実行しました。最初のケース (その後の時間測定) の結果は次のとおりです。

間にシステム コールがないため、遅延は約 260ns であり、プロセス設定による大きな影響はありません。正規分布のタイミングの場合、グラフは、横座標の値が平均で縦座標の値が 0.5 で、傾きが標準偏差を表す直線です。測定値は、より高い遅延の外れ値があるという点で、それとは異なります。

それとは対照的に、2 番目のケース (1 ナノ秒のスリープ) は、システム コールが含まれているため、プロセスのセットアップによって異なります。睡眠時間は非常に短いため、睡眠は時間を追加しません。したがって、グラフにはオーバーヘッドのみが表示されます。

Stéphaneが述べたように、オーバーヘッドはデフォルトで約 64µs です (ここでは少し大きくなっています)。を 10µsに下げることで、時間を約 22µs に短縮timer_slack_nsできます。また、特権を呼び出すことで、オーバーヘッドを約12µs sched_setscheduler()に削減できます。しかし、グラフが示すように、この場合でも遅延は 50µs よりも長くなる可能性があります (実行の 0.0001%)。

測定値は、プロセス設定からのオーバーヘッドの基本的な依存関係を示しています。他の測定では、非 GUI XEON サーバー システムでは変動が 1 桁以上小さいことが示されています。

于 2020-02-10T15:20:39.373 に答える