Vb.net 刻度不一致且波动

Vb.net 刻度不一致且波动,vb.net,visual-studio,debugging,time,stopwatch,Vb.net,Visual Studio,Debugging,Time,Stopwatch,我有一个潜艇和一个按钮。代码是这样的: Private Sub plus(ByRef a As Integer) For i = 0 To a a = a + a Next End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim i As Integer = 19

我有一个潜艇和一个按钮。代码是这样的:

Private Sub plus(ByRef a As Integer)
    For i = 0 To a
        a = a + a
    Next
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim i As Integer = 19
    Dim sw As New Stopwatch
    sw.Start()
    plus(i)
    sw.Stop()
    MsgBox(sw.ElapsedTicks)
End Sub
当我运行sub时,点击“按钮1”,程序输出310,这意味着310是sw.elapsedticks


当我再次单击“按钮1”再次运行sub时,要输出1秒表的程序将退回到使用
DateTime
类,如果您的硬件不支持高性能计数器,则使用刻度进行测量。现在的大多数计算机,至少在Windows 2000及更高版本中,都有高性能计数器。考虑到这一点,.NET
秒表
类基于此高频计时器。通常,调用
Start
查询性能计数器并存储值。当您停止时,它会再次查询性能计数器。然后,经过的时间是这两个值的简单减法,以获得
ElapsedTicks

以下是一些需要进一步解释的项目…

此属性只调用了
GetRawElapsedTicks()

下面的函数返回从秒表启动到调用stopwatch
Stop
方法所经过的实际时间。如上所述,
经过的时间是这两个值的简单减法
您可以看到以下内容:
currentTimeStamp-startTimeStamp

 // Get the elapsed ticks.        
#if FEATURE_NETCORE
        public long GetRawElapsedTicks() {
#else
        private long GetRawElapsedTicks() {
#endif
            long timeElapsed = elapsed;

            if( isRunning) {
                // If the StopWatch is running, add elapsed time since
                // the Stopwatch is started last time. 
                long currentTimeStamp = GetTimestamp();                 
                long elapsedUntilNow = currentTimeStamp - startTimeStamp;
                timeElapsed += elapsedUntilNow;
            }
            return timeElapsed;
        }  
您可能会注意到上面的
#if功能_NETCORE
,并想知道它是什么。这些命令称为预处理器命令。基本上,它们的工作方式类似于
if-else
语句,但如果不满足条件,则不会在编译时包含代码,因为这是预编译决定,而不是运行时

综上所述,Hans Passant已经提到了JIT编译。我上面提到的所有这些都将其进一步分解以进行解释。差异的真正原因是它第一次编译和运行所花的时间

还有一件事要提。
stopwatch
类使用一个
long
变量类型来存储他们所称的
frequency
。频率”存储高分辨率性能计数器的频率(如果存在),否则将存储TicksPerSecond。在系统运行时,频率无法更改,因此仅初始化一次。

下面是其他的
常数
,它们与频率及其计算方式有很大关系

 private const long TicksPerMillisecond = 10000;
 private const long TicksPerSecond = TicksPerMillisecond * 1000;
当您创建一个新的
StopWatch
类实例时,这就是所运行的

bool succeeded = SafeNativeMethods.QueryPerformanceFrequency(out Frequency);            
            if(!succeeded) {
                IsHighResolution = false; 
                Frequency = TicksPerSecond;
                tickFrequency = 1;
            }
            else {
                IsHighResolution = true;
                tickFrequency = TicksPerSecond;
                tickFrequency /= Frequency;
            }

正如您现在所看到的,频率在设置运行时间的计算方式以及勾选频率方面起着重要作用。即使在停止应用程序时,也无所谓,因为此时存储了频率。重置频率的唯一方法是重新启动m机器。

我假设原因与
快速排序
方法中的代码有关。真的吗?我在新项目vb.net中再次测试,并制作了非常简单的sub。同样的问题也发生了!!当我调用sub确定时,需要200个刻度,因为sub非常简单。当我再次调用该sub时,只需要3个刻度。然后编辑你的q包含非常简单的sub。事实上,它缺少回答问题所需的功能。我猜它与CPU缓存有关。这是一篇很棒的文章。对于我们的案例,尤其是后台部分。在方法开始运行之前,必须首先从程序集中的MSIL及时编译到ma处理器可以执行的中文代码。这种情况只发生一次,即您第一次调用该方法时。您可以看到抖动完成该任务所需的时间。使用提前编译器(Ngen.exe)进行实验看到差异。所有这些关于频率的问题与这个问题有什么关系?他不是问秒表类是如何工作的,他是问为什么他在连续调用一个方法时看到时间的差异。这里唯一一段真正回答这个问题的是你提到Hans评论的那一段。@CodyGray it h这和问题有很大关系。这不仅仅是该方法第一次运行。它使用的是它第一次运行时存储的频率。我在我的帖子中解释了这一点。如果你读了最后一部分,我会解释这一点。你自己试试,第一次实例化该类时,它会存储频率。在运行相同的t时间大大缩短。方法单击一次并不重要。您可以停止应用程序并再次运行,结果仍然较低!原因是它查看存储的频率!哦,我明白您的意思。在Stopwatch类实例的第一次创建时,它的静态构造函数必须运行,这不需要花费时间这是真的(事实上,C#中的静态构造函数非常慢,因为编译器没有很好地优化它们),但此初始化不会影响定时测量,因为在调用
start
函数之前不会记录启动时间。他的代码只测量
start
Stop
之间经过的时间(这实际上是调用函数
plus
所需的时间)。是的,如果你在重新启动的机器上运行,你会得到非常高的滴答数。之后的任何操作都基于当前频率。查找它,获取时间戳。它在Microsoft的引用中。我发布的第一个块对此进行了调用…至少新机器支持高频率的旧机器代码到达日期我……我们很幸运;)
bool succeeded = SafeNativeMethods.QueryPerformanceFrequency(out Frequency);            
            if(!succeeded) {
                IsHighResolution = false; 
                Frequency = TicksPerSecond;
                tickFrequency = 1;
            }
            else {
                IsHighResolution = true;
                tickFrequency = TicksPerSecond;
                tickFrequency /= Frequency;
            }