ひとやすみ中と書いたばかりですが
いつのまにかPython初心者に戻っている、かえるのクーの「井戸中 聖」(いとなか せい)です。
いちおうエンジニアの端くれですが、Pythonは趣味でしか書いていないので、忘れてしまいました。先日近況報告したのが呼び水になって(気楽になったので)、すこしだけ書いてみます。
リハビリの「お題」
『Pythonでどれだけ「リアルタイム性」のあるプログラミングができるか』です。音楽系のプログラミングもやるので、20KHz周期の±1/8(角度で±45°)くらいの精度がほしいところですが、リアルタイムが超絶苦手なWindowsでどこまでできるかやってみます。
ちなみに時間でいえば±6.25μSec( 0.00625mSec)くらいの精度です。
なお、WindowsはOSで使用するタイマーが内部Clock関数ベースで 15mSec程度、タイマを使用しないQueryPerformanceCounterで2μSec程度といわれています。Sleep系はClockベースと聞いたことがあるので、注意が必要のようです。
要求仕様
・精度の高いタイミングを導出するしくみを確立する
・時間待ちのときはむやみにCPU負荷をあげたくない。
・ただし精度が必要な局面ではCPUパワーで解決してもよい。
・個人的に音楽系のソフトはWindows系のものばかりなので、OSはWindowsで動作させるものにしたい。(今回Windows限定なのでご容赦ください)
コンセプト
・ある程度までの時間待ちはsleep系で行う
・ターゲットタイミング付近ではpythonで最も精度の高い時間計測関数を使用し、あとはCPUパワーで解決する。
基礎計測
Sleep系の最小単位
いろいろ記事をみてもはっきりとはわかりませんが、sleepはOSのタイマー精度に依存していることは間違いないようです。
上記の解説にあるタイマの変更は音楽系のソフトには厳禁なので行いません。別の方法を模索します。
計測しました!
sleep(0)の時間計測(1000回測定)は 6.25±5.5μSec 平均値 0.8064μSecでした。結構偏りがあります。
import time
max_time = 0.0
min_time = 1.0
total_time = 0.0
range_size = 1000
for num in range(range_size):
start_time = time.perf_counter()
time.sleep(0.0) #ここをコメント化したり、値を変えたりする
elapsed_time = time.perf_counter() - start_time
if elapsed_time > max_time:
max_time = elapsed_time
if elapsed_time < min_time:
min_time = elapsed_time
total_time += elapsed_time
print("max:{:.10f}, min:{:.10f}".format(max_time, min_time))
print("mid:{:.10f}, ave:{:.10f}".format((max_time + min_time)/2, total_time/range_size))
print(time.perf_counter())
sleep(0.0000001) (0.1μSec指定)の時間計測(1000回測定)は15922.5±11284.8μSec 平均値16295.46μSecでした。ちなみにsleep(0.01)(10mSec指定でも上記とほぼ同じ結果となりました。僅かな値を設定しただけでやはりタイマー精度程度は「ブレる」ようです。
sleep(0.18)~sleep(0.3)では上記sleep(0.0000001)またはsleep(0.01)の約2倍の数値でした。
Sleep「ゼロ」は測定からタイマ待ちはしていないと思われますが、処理の前処理・後処理だけでこれくらいかかるようです。 sleep(0)はCPUやマザーボードに依存するとは思いますが、私の環境では1~12μSec程度の幅の時間待ちには代用できそうなことがわかりました。
よってSleepは概ね 「15.625×(N±1) + 0.01」 mSec 程度の精度?と思われます。
1 Sleep単位(15mSec)であれば、「音出し」に関しては許容範囲ですが、2 Sleep単位(30mSec程度)であれば、音楽ソフトの「遅延」として「もっさり感」が私でもわかるくらいなので、注意して使う必要があります。
時間計測の最小単位
Pythonの perf_counter() は、Windows系のもっとも解像度が高いといわれるWin32(64)のQueryPerformanceCounter と繋がっているようです。
Pythonの perf_counter()の精度(1000回測定)は 0.1±0.1μSecでした
どうやら最小分解能は0.1μSec 誤差も0.1μSec とみてよいようです。
参考にした情報より1桁精度が高いですが、こういうのもCPUやマザーボードに影響されているのかもしれません。
以上より、音響/音楽系の制御としては充分な精度のタイミングが導出できそうな感じがします。
お題はメトロノームにします
明日も幸い時間があるので、『お題』に沿ってメトロノームを作成してみたいと思います。
祝 人類に「かえる教徒」絶賛増加中
飼育するときは、やさしくしてあげてね。カビ類には超絶弱いので適度に清潔にお願いします。とはいえ、塩素系消毒は(残留リスクがあるので)厳禁です!
ではまた。