dotnet下時間精度測量

2
static void Main(string[] args) 3
{ 4
long start,end; 5

6
start = DateTime.Now.Ticks; 7
end = DateTime.Now.Ticks; 8

9
while(start==end) 10
{ 11
end = DateTime.Now.Ticks; 12
} 13

14
Console.WriteLine("DateTime.Now.Ticks時間精度:{0}ms",(end-start)/10000); 15
}16

運行結(jié)果:DateTime.Now.Ticks時間精度:10ms
yinh找到了一個程序QueryPerfCounter,可以進行精確的時間測量。代碼如下:
using System; 2
using System.ComponentModel; 3
using System.Runtime.InteropServices; 4

5
public class QueryPerfCounter 6
{ 7
[DllImport("KERNEL32")] 8
private static extern bool QueryPerformanceCounter( 9
out long lpPerformanceCount); 10

11
[DllImport("Kernel32.dll")] 12
private static extern bool QueryPerformanceFrequency(out long lpFrequency); 13

14
private long start; 15
private long stop; 16
private long frequency; 17
Decimal multiplier = new Decimal(1.0e9); 18

19
public QueryPerfCounter() 20
{ 21
if (QueryPerformanceFrequency(out frequency) == false) 22
{ 23
// Frequency not supported 24
throw new Win32Exception(); 25
} 26
} 27

28
public void Start() 29
{ 30
QueryPerformanceCounter(out start); 31
} 32

33
public void Stop() 34
{ 35
QueryPerformanceCounter(out stop); 36
} 37

38
public double Duration(int iterations) 39
{ 40
return ((((double)(stop - start)* (double) multiplier) / (double) frequency)/iterations); 41
} 42
}43

我搜了一下,該程序原出處應(yīng)該是http://channel9.msdn.com/wiki/default.aspx/PerformanceWiki.HowToTimeManagedCode
采用以下代碼測試其時間精度:
static void Main(string[] args) 2
{ 3
QueryPerfCounter q = new QueryPerfCounter(); 4

5
q.Start(); 6
q.Stop(); 7

8
Console.WriteLine("QueryPerfCounter時間精度:{0}ns",q.Duration(1)); 9
}結(jié)果:
QueryPerfCounter時間精度:3911.1116077602ns
如果測試之前這么來一下:
// Call the object and methods to JIT before the test run
QueryPerfCounter myTimer = new QueryPerfCounter();
myTimer.Start();
myTimer.Stop();
結(jié)果大概是1微妙。
也就是說QueryPerfCounter的測試精度是1微妙,這主要是因為方法調(diào)用和執(zhí)行要花去一些時間。為了得到更精確的時間測量,必須對此進行時間校準,扣除方法調(diào)用和執(zhí)行的時間.我在構(gòu)造函數(shù)里面加了校準項。完整代碼如下:
using System;2
using System.ComponentModel; 3
using System.Runtime.InteropServices;4

5
public class QueryPerfCounter 6
{ 7
[DllImport("KERNEL32")] 8
private static extern bool QueryPerformanceCounter( 9
out long lpPerformanceCount); 10

11
[DllImport("Kernel32.dll")] 12
private static extern bool QueryPerformanceFrequency(out long lpFrequency); 13
14
private long check;15
private long start; 16
private long stop; 17
private long frequency; 18
Decimal multiplier = new Decimal(1.0e9); 19

20
public QueryPerfCounter() 21
{ 22
if (QueryPerformanceFrequency(out frequency) == false) 23
{ 24
// Frequency not supported 25
throw new Win32Exception(); 26
}27
28
check = 0;29

30
QueryPerformanceCounter(out start);31
QueryPerformanceCounter(out stop);32

33
QueryPerformanceCounter(out start);34
QueryPerformanceCounter(out stop);35
check+=stop-start;36
} 37

38
public void Start() 39
{ 40
QueryPerformanceCounter(out start); 41
} 42

43
public void Stop() 44
{ 45
QueryPerformanceCounter(out stop); 46
} 47

48
public double Duration(int iterations) 49
{ 50
return ((((double)(stop - start-check)* (double) multiplier) / (double) frequency)/iterations); 51
} 52
}53

采用下面這個方法測試校準效果:
static void TestCheckPrecision()2
{3
QueryPerfCounter q;4

5
int loop = 10000;6
int exCount = 0;7

8
for (int i=0;i<loop;i++)9
{10
q = new QueryPerfCounter();11
q.Start();12
q.Stop();13
if(q.Duration(1)!=0)14
{15
exCount++;16
Console.WriteLine("QueryPerfCounter時間精度異常:{0}ns",q.Duration(1));17
}18
}19

20
Console.WriteLine("共進行{0}次QueryPerfCounter的時間精度測試",loop);21
Console.WriteLine("其中{0}次QueryPerfCounter的時間精度異常",exCount);22
Console.WriteLine("時間校準有效性{0}%",100-(100.0*exCount)/loop);23
}24

QueryPerfCounter時間精度異常:-838.095344520044ns
QueryPerfCounter時間精度異常:-558.730229680029ns
QueryPerfCounter時間精度異常:-558.730229680029ns
QueryPerfCounter時間精度異常:-838.095344520044ns
QueryPerfCounter時間精度異常:-838.095344520044ns
QueryPerfCounter時間精度異常:-838.095344520044ns
QueryPerfCounter時間精度異常:-838.095344520044ns
QueryPerfCounter時間精度異常:-558.730229680029ns
QueryPerfCounter時間精度異常:-838.095344520044ns
QueryPerfCounter時間精度異常:-838.095344520044ns
QueryPerfCounter時間精度異常:-558.730229680029ns
QueryPerfCounter時間精度異常:-558.730229680029ns
QueryPerfCounter時間精度異常:-838.095344520044ns
QueryPerfCounter時間精度異常:-345854.012171938ns
QueryPerfCounter時間精度異常:279.365114840015ns
QueryPerfCounter時間精度異常:-55314.2927383229ns
QueryPerfCounter時間精度異常:279.365114840015ns
QueryPerfCounter時間精度異常:5307.93718196028ns
QueryPerfCounter時間精度異常:279.365114840015ns
QueryPerfCounter時間精度異常:279.365114840015ns
QueryPerfCounter時間精度異常:279.365114840015ns
共進行10000次QueryPerfCounter的時間精度測試
其中22次QueryPerfCounter的時間精度異常
時間校準有效性99.78%
也就是說99.78%情況下都得到了很好的校準,只有極少數(shù)情況校準會出現(xiàn)異常現(xiàn)象不為零,有時這一波動甚至很大,到了5.3微妙。這個結(jié)果很滿意了,因為異常的測試結(jié)果可以很簡單的發(fā)現(xiàn)出來。但是是不是說明,校準后的類的測量誤差就差不多就是納秒級的呢?我后來又做了一個測試發(fā)現(xiàn)不是這樣,測試代碼如下:
static void TestPrecision()2
{3
QueryPerfCounter q;4

5
for(int j=0;j<200;j++)6
{7
q = new QueryPerfCounter();8

9
q.Start();10
q.Stop();11

12
Console.WriteLine(""); 13

14
Console.WriteLine("QueryPerfCounter時間精度:{0}ns",q.Duration(1)); 15

16
q.Start();17
int i=0;18
for(i=0;i<j;i++)19
{20
int l;21
}22
q.Stop();23

24
Console.WriteLine("第{0}次循環(huán),耗時{1}ns",i,q.Duration(1));25
}26
}27

對于循環(huán)
for(i=0;i<j;i++)
{
int l;
}
當j很小的時候,耗時為0ns,隨著n逐漸增大,耗時會一下子跳到279.365114840015ns,然后是558.730229680029ns。我摘幾條結(jié)果:
QueryPerfCounter時間精度:0ns
第73次循環(huán),耗時0ns
QueryPerfCounter時間精度:0ns
第74次循環(huán),耗時279.365114840015ns
QueryPerfCounter時間精度:0ns
第75次循環(huán),耗時0ns
QueryPerfCounter時間精度:0ns
第76次循環(huán),耗時279.365114840015ns
QueryPerfCounter時間精度:0ns
第88次循環(huán),耗時558.730229680029ns
QueryPerfCounter時間精度:0ns
第89次循環(huán),耗時279.365114840015ns
也就是說,對我的機器現(xiàn)在配置來說(1.5G迅馳,2003 server,.net framework 1.1),測量的最基本時間單位是279.365114840015ns,不能得出更精確的時間了。
因此,推薦的測試方法是采用校對后的QueryPerfCounter,多測量幾次,去掉最高值,去掉最低值(這兩個一定要去掉),然后取平均值,這樣能夠得到非常精確的測量結(jié)果,測量誤差約為0.3微妙。



浙公網(wǎng)安備 33010602011771號