数分の1秒間スリープするループが必要


13

私のマシンでは、1秒の小数で表される遅延を持たなければならない1つの単純なコマンドを繰り返すサイクルを実行する必要があります。

私が必要だとしましょう:

  • 列挙を増やしてファイルを保存するには(ファイル-0、ファイル-1、ファイル-2、...) time > file-$x
  • ほんの一瞬で自分の時間を表現したいので、これを1/70秒ごとに(例として)行う必要があります。

どうすれば本当に正確になり、すべてをbashスクリプトで表現できますか?

分数は不確定な量を生成する可能性があるため、正確である必要があるため、少なくとも4〜5桁の小数が必要です。

回答:


11

bashで分数から小数に変換するには、次のようにします

myvar=$(echo "scale=4; 5/10" | bc)

次に、その値でループを実行するには、

for i in $(seq 1 1000); do sleep $myvar; done

Debian(GNU)での私のスリープ実装は、10進数のスリープ値を受け入れるようです。

残念ながら…

そのような精度(小数点以下4〜5桁)を使用すると、perlスクリプトまたはコンパイル済みプログラムのようなものが必要になります。ループ内でプログラムを呼び出すオーバーヘッドにより、多くのジッターが追加されます。スリープ自体の呼び出しには数ミリ秒かかります。

1000回行われる次の1 / 10,000秒のスリープを考慮してください。

time for i in $(seq 1 1000); do sleep 0.0001; done

real    0m2.099s
user    0m3.080s
sys     0m1.464s

予想される結果は1/10秒です。睡眠はあなたが望む寛容に近いところはありません。

/programming/896904/how-do-i-sleep-for-a-millisecond-in-perl

perlのTime :: HiResを使用して、1000 * 1000マイクロ秒:

my $i=0;
for($i=0;$i<=1000;$i++) {
        usleep(1000);
}

real    0m1.133s
user    0m0.024s
sys     0m0.012s

1秒にずっと近づきます。


小数点以下4〜5桁を達成できない場合は、代替案として可能な限り最良の結果が得られますが、私のポイントは、小数点以下または秒ではなく端数で表現する必要があるということです。
user1717079

bashを含むスクリプト言語で、分数を小数に変換することはまったく簡単です。例:echo "scale = 4; 5/10" | BC
ロブ・ボス

今私が式を変換する方法を持っている、問題は今...シンプルなbashが、それは本当に遅いですし、単純にこの周波数に追いつくことができないということです
user1717079

ええ、1/10秒よりもはるかに正確なものが必要な場合は、おそらくループ内でオーバーヘッドを呼び出すプログラムを回避するために、perlまたはpythonが必要になるでしょう。
ロブ・ボス

7

たぶん、あなたは単に実行することができます

sleep 0.7

man 1 sleep

私のarchlinuxディストリビューションで:

説明NUMBER秒間一時停止します。SUFFIXは、秒を表す「s」(デフォルト)、分を表す「m」、時間を表す「h」、または日を表す「d」です。NUMBERが整数である必要があるほとんどの実装とは異なり、ここでNUMBERは任意の浮動小数点数です。2つ以上の引数が与えられた場合、それらの値の合計で指定された時間だけ一時停止します。


sleep分数を許可しますか?偽のループの完全な例を提供できますか?
user1717079

0.7私の問題を表現するのは分数ではありません...私の問題は粒度に関するものです。3分の1を考えて、睡眠を正確にしようとします。
user1717079

6

プロセスを生成してその中に新しい実行可能ファイルをロードするのに数ミリ秒かかる可能性が高いため、そのような精度は実際には意味をなしません。また、多くのシステムのCPU時間は、最大10ミリ秒のスライスによってプロセスに割り当てられることに注意してください。

そうは言っても、一部のsleep実装では秒の小数部を使用し、zshとksh93の両方で$SECONDS特別な変数を小数部にすることができますtypeset -F SECONDS

例(zsh):

$ typeset -F SECONDS=0; for ((i=1; i<=70; i++)); do sleep $((1./70)); date +%s.%N; done | { head -n3;echo ..;tail -n3; }; echo $SECONDS
1350076317.374870501
1350076317.391034397
1350076317.407278461
..
1350076318.464585550
1350076318.480887660
1350076318.497133050
1.1393780000

おっと、ドリフトしました。あなたはに基づいて睡眠時間を調整することができます$SECONDS

$ typeset -F SECONDS=0; for ((i=1; i<=70; i++)); do sleep $((i/70. - SECONDS)); date +%s.%N; done | { head -n3;echo ...;tail -n3; }; echo $SECONDS
1350076420.262775654
1350076420.277012997
1350076420.291302750
../..
1350076421.219682227
1350076421.234134663
1350076421.248255685
1.0020580000

これらの余分な2ミリ秒は、おそらく最後のコマンドsleepdateコマンドの実行に起因するはずです。

また、zshにはzselect100分の1秒単位で表されるタイムアウトが組み込まれていることに注意してください。また、ksh93にはsleep組み込み(および浮動小数点を受け入れる)があり、printf日付/時刻を出力できます。

$ typeset -F SECONDS=0; for ((i=1; i<=70; i++)); do ((i<4 || i>67)) && printf '%(%S.%N)T\n' now; sleep $((i/70.-SECONDS)); done; echo $SECONDS
20.823349000
20.837510000
20.851663000
21.780099000
21.794254000
21.808405000
0.9992358685

あなたはより正確な何かをしたい場合は、おそらくリアルタイム機能を備えたリアルタイム・オペレーティング・システムまたはオペレーティング・システムをお勧めしますし、確かにないシェルを使用しています。


sleep 1/70...私のマシン上で許可されていません
user1717079

私が達成したいものにさえ近くない、どうすればより速く物事を実行できますか?
user1717079

申し訳ありませんが、分数で表すと、n / m(nとmは整数)として表現するのではなく、小数部分を持つ小数として表現します。私はあなたにもそれらを呼び出すことができたと浮動小数点数を小数私は適切な英語の用語についてはよく分からないけれども
ステファンChazelas

私は...私はシェルの避けるつもりだと思います
user1717079

回答者は正しいです。シェルとプロセスは通常、ミリ秒の解像度にはあまり適していません。信頼性の高いパフォーマンスを得るには、usleep(3)などを呼び出すプログラムをコンパイルする必要があります。また、異なる構成でカーネルを再コンパイルする必要がある場合があります。または、少なくとも異なるパラメーターをカーネルのランタイムスケジューラーにフィードします。これは簡単ではありません。標準カーネルを使用した標準Debianインストールでは、リアルタイム機能が制限されています。それでも、nice(1)コマンドをチェックアウトすることをお勧めします。役立つかもしれません。
thb

3

シェルsleepが分数を受け入れない場合は、perlを使用してください。

sleep_fraction() {
  /usr/bin/perl -e "select(undef, undef, undef, $1)"
}

sleep_fraction 0.01428

分数を調べる必要がある場合は、使用します echo "scale=5; 1/70" | bc


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.