dotnet下时间测量(续):进行纳秒级测量
还能不能得到更精确的时间呢?那就用汇编吧,通过rdtsc直接取时钟周期数。在Feng Yuan的《Windows图形编程》上找到获取时钟周期的函数,在网上搜索到获取本机CPU主频函数,凑在一起,得到如下代码:
2
3 extern "C"
4 {
5 __declspec(dllexport) unsigned __int64 GetCycleCount(void)
6 {
7 _asm _emit 0x0F;
8 _asm _emit 0x31;
9 }
10
11 __declspec(dllexport) int GetFrequency(void) //MHz
12 {
13 LARGE_INTEGER CurrTicks, TicksCount;
14 __int64 iStartCounter, iStopCounter;
15
16 DWORD dwOldProcessP = GetPriorityClass(GetCurrentProcess());
17 DWORD dwOldThreadP = GetThreadPriority(GetCurrentThread());
18
19 SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
20 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
21
22 QueryPerformanceFrequency(&TicksCount);
23 QueryPerformanceCounter(&CurrTicks);
24
25 TicksCount.QuadPart /= 16;
26 TicksCount.QuadPart += CurrTicks.QuadPart;
27
28 _asm rdtsc
29 _asm mov DWORD PTR iStartCounter, EAX
30 _asm mov DWORD PTR (iStartCounter+4), EDX
31
32 while(CurrTicks.QuadPart<TicksCount.QuadPart)
33 QueryPerformanceCounter(&CurrTicks);
34
35 _asm rdtsc
36 _asm mov DWORD PTR iStopCounter, EAX
37 _asm mov DWORD PTR (iStopCounter + 4), EDX
38
39 SetThreadPriority(GetCurrentThread(), dwOldThreadP);
40 SetPriorityClass(GetCurrentProcess(), dwOldProcessP);
41
42 return (int)((iStopCounter-iStartCounter)/62500);
43 }
44 }
编译为TimeStamp.dll,通过DllImport调用,代码如下(为测试而作,实用得再加几个方法):

2

3

4

5

6

7

8

先测试TimeStamp.GetCycleCount()的执行时间,也既两次GetCycleCount()运行的时间差。测试代码如下:

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

作统计图:
中间有几个10ms的突出点,这应该是测试程序被中断了。
为了避免被外界中断,把当前进程设置为实时进程,所得结果作图如下。
可以看出,i>50后,结果稳定在0.1646微秒。也就是说,必须至少运行100次TimeStamp.GetCycleCount(),然后每次TimeStamp.GetCycleCount()调用的时间才会降低到0.1646微秒,所得的测试结果才比较精确。为安全起见,建议每次测试之前运行至少500次TimeStamp.GetCycleCount()。
比较实时进程和非实时进程运行的前100次结果,得图:
可以看出,开始一次TimeStamp.GetCycleCount()调用运行耗时差不多都在2-3微秒,当i在50附近,非实时进程测试中这一值跳到3-4,然后迅速下降到0.164,实时进程跳到>25,然后下降到0.164。这应该和运行时优化有关。
为了得到更精确的时间,需要对测试结果进行修正,扣除一次TimeStamp.GetCycleCount()调用的时间。测量修正效果,代码如下:

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

32

得到下图:
可以看出,大多数情况下,修正后时间测量误差是-0.0006微秒,也就是-0.6纳秒,一个时钟周期左右!!!!够精确了!!!!!!!!!考虑极少数异常现象,误差也在20纳秒之内,但这些异常点可以通过多次测试去除最高最低点排除在外。
总结:
通过调用rdtsc,并经过时间校正,可以得到纳米级的时间测量工具。
具体步骤如下:
(1)设置运行进程为实时进程;
(2)运行至少100次(最好500次)TimeStamp.GetCycleCount()热热身;
(3)运行500次左右TimeStamp.GetCycleCount()得到TimeStamp.GetCycleCount()调用平均周期数;
(4)进行时间测量,测量结果减去第(3)步中的调用时间
(5)重复第4步n次(n>5)既可,去掉一个最高的,去掉一个最低的,剩下的取平均值,这样测量误差应该小于1纳秒。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义