FC2ブログ

Excelじゆうちょう

Excelのお絵描きツール『りっぷ2(りっぷつぅ)』のサポートページ、まずは「はじめに」をご覧ください。 [NewEntry] [Admin]

記事更新カレンダー

09 « 2018-10 « 11
- 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31 - - -

やたらに多いカテゴリ

比較的新しい記事

新しいコメント

ありがたいブログ拍手

拍手コメント一覧(拍手はしない)

さみしいトラックバック

申し訳ないプロフィール

申し訳ない

管理人  [ 申し訳ない ]

pxivもやってます
リンクの一番上からのぞきに来てください
※閲覧にはユーザー登録が必要です

RSSってなんぞ?

広告は消せないらしい

FC2Ad

        --------       スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

        2009-11-03       高精度Sleepの実装

前回紹介したtimeBeginPeriod関数とtimeEndPeriod関数を調べててみました。

【timeBeginPeriod】
アプリケーションまたはデバイスドライバの最小タイマ分解能を、ミリ秒単位で指定します。

【timeEndPeriod】
以前にセットされた最小タイマ分解能をクリアします。


たった一文なのに、どうしてこうもわかりにくいんでしょうか。
たぶん、こんな理解でいいと思います。

ソフトウェアや周辺機器が時間の処理をする際の、最小の単位をミリ秒単位で設定できる。

デフォルトだと15ミリ秒ですので、Sleep 1でもSleep 5でも同じ1単位に扱われて15ミリ秒かかってしまいます。
そこで、timeBeginPeriodの引数に1を指定して実行することで、それ以降の時間単位を1ミリ秒に変更することができます。
そして、変更したものはちゃんと元に戻すのが礼儀ですので、timeEndPeriodの引数に最初と同じ値(ここでは1)を指定して実行します。

よって、前回のSleepの精度が向上した背景には、Windows Media Playerが起動する際にtimeBeginPeriod 1が実行され、終了する際にtimeEndPeriod 1が実行されていると推測されます。

それでは、これをExcelで実行してみましょう。
ですが、残念ながらこれらの関数はExcelの持っている関数ではありませんので、そのままでは構文エラーとして実行前にはじかれてしまいます。

↓エラーのある行が黄色くなる↓
コンパイルエラー

Excelさんが「そんなプロシージャか関数みたいなの知らないよ!」と怒っているわけです。
そんなExcelさんのために、「これは関数です、使い方はここを見てください」と教えてあげなくてはなりません。
以下がtimeBeginPeriod関数とtimeEndPeriod関数の宣言文になります。
これを、使いたいモジュールの宣言部に記述します。

Private Declare Function timeBeginPeriod Lib "winmm.dll" (ByVal uPeriod As Long) As Long
Private Declare Function timeEndPeriod Lib "winmm.dll" (ByVal uPeriod As Long) As Long


簡単に説明すると、「このモジュールだけで使用できる(Private)、宣言をします(Declare)。これは関数で(Function)、名前は(timeBeginPeriod)。使い方は(Lib "winmm.dll")を参照。引数は整数型(ByVal uPeriod As Long)、戻り値は整数型(As Long)です。」となります。
※この憲章では、戻り値を使っていません

実際に動かしてみましょう。(わかりやすいように、コードの整理はしてません)


'【コードウインドウ】
Private Declare Function timeBeginPeriod Lib "winmm.dll" (ByVal uPeriod As Long) As Long
Private Declare Function timeEndPeriod Lib "winmm.dll" (ByVal uPeriod As Long) As Long

Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
'ここまで、宣言部

Private Sub testSleep()
Dim For0 As Long, For1, Tck0 As Long, Tck1 As Long
For For0 = 1 To 3
  Tck0 = GetTickCount
  For For1 = 1 To 100
    Sleep For0
  Next For1
  Tck1 = GetTickCount
  Debug.Print "デフォルトで、Sleep " & For0 & " = " & Tck1 - Tck0 & " ミリ秒"
Next For0
timeBeginPeriod 1 '処理をする最小の時間単位を1ミリ秒に設定
Sleep 100 '変更直後は精度が不安定になるらしいので、小休止
For For0 = 1 To 3
  Tck0 = GetTickCount
  For For1 = 1 To 100
    Sleep For0
  Next For1
  Tck1 = GetTickCount
  Debug.Print "高精度で、Sleep " & For0 & " = " & Tck1 - Tck0 & " ミリ秒"
Next For0
timeEndPeriod 1 '処理をする最小の時間単位をデフォルトに設定
Sleep 100 '変更直後は精度が不安定になるらしいので、小休止
For For0 = 1 To 3
  Tck0 = GetTickCount
  For For1 = 1 To 100
    Sleep For0
  Next For1
  Tck1 = GetTickCount
  Debug.Print "デフォルトで、Sleep " & For0 & " = " & Tck1 - Tck0 & " ミリ秒"
Next For0
End Sub

【イミディエイトウインドウ】
デフォルトで、Sleep 1 = 1560 ミリ秒
デフォルトで、Sleep 2 = 1560 ミリ秒
デフォルトで、Sleep 3 = 1560 ミリ秒

高精度で、Sleep 1 = 94 ミリ秒
高精度で、Sleep 2 = 202 ミリ秒
高精度で、Sleep 3 = 297 ミリ秒

デフォルトで、Sleep 1 = 1560 ミリ秒
デフォルトで、Sleep 2 = 1560 ミリ秒
デフォルトで、Sleep 3 = 1560 ミリ秒



うむ、バッチリですね
スポンサーサイト

        2009-11-01       Windows API、高精度のSleep

Sleepの精度をほぼ1ミリ秒にする方法が見つかりました。
とりあえず、こちらの検証結果をご覧ください。

長くなるのでこちらを参照(TXT)

実行時間がほんのわずかに短くなっているようですが、十二分に許容範囲です。
さて、その方法とは…「Windows Media Playerを起動しておく」です。

発見したのは、本当に偶然でした。
速度比較をする時は、他のアプリケーションの影響を極力受けないように閉じておきます。
前回の検証でもそうしていました。
しかし、その後音楽を聴きながらマクロをもう一度実行してみると、上のような結果になりました。
この時、同時に複数のアプリケーションを起動していたので、とにかく組み合わせを総当たりで調べ、Windows Media Playerへ辿り着きました。
音楽や動画を再生する必要はありません、起動さえしていればOKです。

私が普段使用しているプレーヤはGOM Playerなのですが、こちらでは効果はありませんでした。
本当に偶然です、よくもまあうまい具合にWindows Media Playerを立ち上げていたものです。

それにつけても不思議です。
GOM Playerよりもはるかに動作の遅いWindows Media Playerがどうして、Sleepの精度を上げるのでしょうか。

これまたGoogleで検索してみたところ、APIのtimeBeginPeriod関数で指定した値の精度に設定することができるとのことらしいです。
そして、timeEndPeriod関数で変更を元に戻すようです。
これ以上の説明は…今の私では自信がないので控えておくことにします。

likePへの応用アリアリですね、勉強せねば!



【おまけ】
Sleepの引数に0を指定した場合の実行時間を測定してみました。
100回のループでは0ミリ秒とありましたが、そんなはずはありません。
きっと、誤差の範囲で測定できないくらい短いのでしょう。
もっとループ数を増やせば測定できるはずです。

1億回ループしました

(27487-2060)/100000000=0.00025427ミリ秒
ナノ秒の世界ですね、100ループ程度では0ミリ秒になるのも納得です。

        2009-10-30       Windows API、Sleepの精度

Excelでゲームを作成する際に必ずと言っていいほどお世話になるのが、APIのSleep関数です。

これは一定時間マクロの実行を停止するもので、ミリ秒を単位とする整数を引数にとります。
例えば、1秒間だけSleepさせるなら「Call Sleep(1000)」とし、5秒なら「Call Sleep(5000)」となります。
もちろんですが、マイナスの時間を指定することはできません。
アニメーションのスピードを調節したい時、CPUに休息を与えて高負荷を解消したい時などにSleepを活用します。
かくゆうlikePも、ペンの間隔の部分でお世話になっています。

今回私が疑問を持ったのは、Sleepの精度についてです。

Sleepは引数通りの時間を正確に休んでいるのか。
若干ながらでも呼び出す際のオーバーヘッドがあるにしろ、「Call Sleep(1)」は本当に1ミリ秒なのかどうかを検証します。
そこで、2009-09-10の記事『VBA速度比較、ひな形』で紹介したマクロを使います。


'【コードウインドウ】
Private Sub test0()
Dim For0 As Long, For1, Tck0 As Long, Tck1 As Long
For For0 = 0 To 200
  Tck0 = GetTickCount
  For For1 = 1 To 100
    Sleep For0
  Next For1
  Tck1 = GetTickCount
  Debug.Print "Sleep " & For0 & " = " & Tck1 - Tck0 & " ミリ秒"
Next For0
End Sub


さて、その結果は。

長くなるので、こちらを参照(TXT)

驚くことに、ものすごく荒い精度だという結果が出ました。
その精度は15~16ミリ秒で、1ミリ秒でも15ミリ秒でも、Sleepしている時間は同じになりました。

つまり、これまでSleepを使ったマクロで、「この部分は早く処理したから、Sleep 5」「こちらはゆっくりでいいから、Sleep 15」と使い分けていたのが無意味だったことが判明しました。
likePでも、ペンの間隔で同じことがどんぴしゃりで当てはまります。

では、なぜこのようなことが起きるのでしょうか。
Googleで検索をかけてみると、タイマ割り込みの時間が10~15ミリ秒のため、Sleepもその倍数になるということのようです。

とすると、私のPCのタイマ割り込みは15ミリ秒だということになります。
私のPCはそこそこハイスペックだと思ってたのですが、それとはまた違う話なのでしょうか。
それとも、どうにかしたら10ミリ秒にする設定とかがあるのでしょうか。
私もまだまだ勉強不足です、ほんと難しいです。

 | HOME | 

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。