.NETでの速度計測はいろいろあるのでご紹介します。
速度計測するコンポーネントの雛形を決めておきます。
ISpeedCount.cs
namespace SpeedCount { public interface ISpeedCount { void Start(); double ElapsedMilliSec(); string Format(); } }
ここでは下記、3つの方法でやってみます。
- StopWatchクラスを使う場合
- DateTimeクラスを使う場合
- Win32APIを使う場合
1.StopWatchクラスを使う場合
.NETのSystem.Diagnostics名前空間にあるStopWatchクラスそのものです。StartNewでオブジェクトを作成し、Stopまでの時間を計測することができます。
TimeCountByStopWatch.cs
using System.Diagnostics; namespace SpeedCount { public class TimeCountByStopWatch : ISpeedCount { private Stopwatch stopWatch; public void Start() { stopWatch = Stopwatch.StartNew(); } public double ElapsedMilliSec() { stopWatch.Stop(); return (double)stopWatch.ElapsedMilliseconds; } public string Format() { return "StopWatch..{0}ms"; } } }
2.DateTimeクラスを使う場合
DateTimeクラスは日付計算に用いますが、DateTime.Nowというプロパティが”日付”というには精度が良いので使えそうです。
TimeCountByDateTime.cs
using System; namespace SpeedCount { public class TimeCountByDateTime : ISpeedCount { private DateTime startDate; public void Start() { startDate = DateTime.Now; } public double ElapsedMilliSec() { DateTime endDate = DateTime.Now; TimeSpan diff = endDate - startDate; return diff.Duration().Milliseconds; } public string Format() { return "DateTime..{0}ms"; } } }
3.Win32APIを使う場合
Windows環境で速度計測といったらWindows Embeddedでも、QueryPerformanceCounter, QueryPerformanceFrequencyです。 QueryPerformanceCounterでは高精度タイマーの値を返します。高精度タイマーがサポートされていない場合はFALSEが 戻るようですが、いまだかつてそのような環境を見たことがありません(私は)。
TimeCountByPerformanceCounter.cs
using System.Runtime.InteropServices; namespace SpeedCount { public class TimeCountByPerformanceCounter : ISpeedCount { [DllImport("kernel32.dll")] static extern bool QueryPerformanceCounter(ref long lpPerformanceCount); [DllImport("kernel32.dll")] static extern bool QueryPerformanceFrequency(ref long lpFrequency); private long startCounter; public void Start() { QueryPerformanceCounter(ref startCounter); } public double ElapsedMilliSec() { long stopCounter = 0; QueryPerformanceCounter(ref stopCounter); long frequency = 0; QueryPerformanceFrequency(ref frequency); return (double)(stopCounter - startCounter)*1000.0 / frequency; } public string Format() { return "QueryPerformanceCounter/Frequency..{0}ms"; } } }
これらを使ってみるとこんな感じ。
Program.cs
using System; using System.Collections.Generic; namespace SpeedCount { class Program { static void Main(string[] args) { var speedCounters = new List{ new TimeCountByStopWatch(), new TimeCountByDateTime(), new TimeCountByPerformanceCounter() }; foreach (var speedCounter in speedCounters) { speedCounter.Start(); for (int i = 0; i < 2000000; i++) { ; // do nothing. } var val = speedCounter.ElapsedMilliSec(); Console.WriteLine(speedCounter.Format(), val); } Console.WriteLine("press enter to exit."); Console.ReadLine(); } } }
出力は以下のような感じになりました。
DateTime.Now < StopWatch < QueryPerformanceCounter の順に精度が良くなるようですね。参考程度に言うなら、DateTime.Nowは15ms単位、StopWatchは1ms単位、QueryPerformanceCounterは1usはでてそうです。
これを使ってThread.Sleep(1)の時間を測ってみると、じぇんじぇん1msではないことがよくわかります(笑