【问题标题】:How to get ticks from QueryPerformanceCounter in C#?如何从 C# 中的 QueryPerformanceCounter 获取刻度?
【发布时间】:2012-03-20 15:12:26
【问题描述】:

我需要替换 Stopwatch 以避免对其属性使用 getter。我将使用 QueryPerformanceCounter 来实现它。 我只需要滴答声没有别的。

任何人都可以提供代码片段,以获得正确的刻度(毫秒的 1/10000)或任何其他小但稳定的值。

请注意,我的服务器将时钟粒度设置为 0.5 毫秒(不确定它是否会影响 QueryPerformanceCounter),但仅供您了解。

另请注意 - 我不需要计时器。我只需要测量部分代码之间的时间间隔。

编辑: 为避免混淆,我真的很想知道 QueryPerformanceCounter(out long lpPerformanceCount); 中的 lpPerformanceCount 是什么

【问题讨论】:

  • 如果您要测量部分代码之间的时间间隔,这与计时器有何不同?无论哪种方式,您都可以使用 interop 自己调用 Kernel32.dll 的QueryPerformanceCounter
  • 您能否解释一下为什么要这样做“以避免在其属性中使用吸气剂”并没有太多理由。 QueryPerformanceCounter 的问题在于它以计数为单位返回它的值,并且每秒计数的数量因架构而异,并且可以使用 QueryPerformanceFrequency 找到。使用秒表,您可以获得实际时间单位的价值。
  • @BenRobinson - 这对我来说很有意义,否则我不会问。
  • @Bobb 我很长一段时间以来一直是堆栈溢出的活跃成员,我不得不说,仅仅因为有人提出问题并不意味着它有意义,实际上是正确的问题或基于基于有效的假设。我们不是你的主题,你的推理的一些解释可以帮助你解决你的实际问题(你没有解释),而不是仅仅回答可能是或可能不是错误的问题。跨度>
  • 这是一个开放社区。我有疑问,这与您在这里待了一段时间无关,您认为这是对还是错。你没有答案?请继续前进。

标签: c# .net-4.0


【解决方案1】:

http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.gettimestamp.aspx

Stopwatch.GetTimestamp

您不必实例化 Stopwatch 对象,GetTimestamp 应返回刻度数,而不管其上下文如何。

【讨论】:

  • 也可以使用DateTime.Ticks。
【解决方案2】:
[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);

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

取自http://www.codeproject.com/Articles/2635/High-Performance-Timer-in-C

旧的,但它仍然可以工作

编辑:StopWatch 的内部实际上使用了QueryPerformanceCounter,因此使用托管代码应该提供相同的结果,并具有更好的兼容性。

【讨论】:

  • 是的,我知道。从我在网上找到的所有内容,包括您所指的代码项目 - 它显示秒数并需要除以“频率”......我这个问题的重点实际上是找出它返回的值是什么以及如何使用它没有频率。我准备让它依赖于一个没有便携性的特定设置(不在乎)。只需要了解 lpPerformanceCount 实际上是什么。
  • lpPerformanceCount 是滴答数(每个系统都不同),lpFrequency 是每秒滴答数。要获得实际时间,您可以将lpPerformanceCount 除以lpFrequency 以获得以秒为单位的数量。 qpc(out time1); ...; qpc(out time2); long diff_in_ms = (time2-time1) * 1000 / frequency
  • 澄清一下,我的意思是每个系统的频率都不同。
  • @Matthew,至于你的公式——你为什么将结果乘以 1000?由于频率是滴答声/秒,您只需将滴答声乘以频率即可得到以秒为单位的时间跨度。
  • @astrowalker 好点,变量被称为diff_in_ms。我猜我从所需的毫秒数中复制了它。
【解决方案3】:

未经测试,请勿将秒表用于高分辨率计时。自从我测试它已经有几年了,但当时它提供了默认的 windows 时间分辨率。您可以说出来,因为如果使用默认的 windows 分辨率,真的非常快的事情将花费 0 时间或 10-15 毫秒(默认分辨率实际上是线程上下文切换的分辨率,大概是在每个上下文中更新默认 windows 时钟开关,大约每 10-15 毫秒)。

【讨论】:

【解决方案4】:

我使用以下类,在我的系统上将时间精确到 100ns。

public class MStopwatch : Stopwatch
{
    private double _frequencyScaler;

    public MStopwatch()
    {
        _frequencyScaler = 1.0 / Stopwatch.Frequency;
    }

    public double ElapsedSeconds
    {
        get
        {
            return ElapsedTicks * _frequencyScaler;
        }
    }

}

【讨论】:

    【解决方案5】:

    不是一个直接的答案(但可能需要的支持信息如下):

    提供的一些答案非常适合直接回答您的问题。但是,为了完成您想要做的事情,我想补充几点说明。

    首先,考虑运行时发生的即时 (jit) 编译器的时序开销。获取初始时间戳然后执行 stuff 的任何代码,然后获取最终时间戳以减去 t2-t1 的增量,对于 stuff 内的任何功能,如果你包括您在当前过程中尚未调用的任何函数,然后在您第一次调用它时,您将支付将字节码编译为本机代码的 jit 开销。在这种情况下,成本不代表性能关键代码的实际运行时成本,这可能是频繁调用但其 jit 成本仅支付一次的代码(在流程中第一次调用时)。所以多次调用定时代码,丢掉第一个定时,取一个平均值。

    此外,请注意垃圾收集器的运行时成本。如果您只是在玩耍,那么对严格避免分配新对象的代码进行基准测试可能会很有趣。但有时这并不容易,特别是当您调用无法更改其实现的函数时。另外,我必须承认,生产代码的真实世界性能开销无法避免垃圾收集开销,因此获得一个现实的数字应该包括这个开销。也就是说,如果您的 stuff 包含分配新对象的代码,那么它可能会启动垃圾收集器,这将是昂贵的,因此请为您可能想要丢弃的一些潜在异常值做好准备。

    其次,好的答案提供了声明外部函数以从系统 dll 调用的代码,这很好。在一般情况下正确获取这些签名有时会很麻烦,所以我想为此提一个很好的资源:pinvoke.net。 对 QueryPerformanceCounter 的搜索给了我一些签名,使我与已经给出的答案相距甚远,它是您想要进行的任何系统调用的绝佳资源。

    http://pinvoke.net/search.aspx?search=QueryPerformanceCounter&namespace=[All]

    【讨论】:

    • 也许它对非 .net 专业人士有用。衡量绩效的重点就是——衡量绩效。如果您的代码在 GC 上等待 - 它必须反映在统计数据中.. 这不是一个科学项目.. 如果在实践中您的应用程序在不应该遇到 GC 时遇到了问题 - 这就是必须 a) 发现的问题(使用我的 perfcounters在问)b)由开发人员解决(不同的故事)。感谢您对使用内核对象的想法。但我不认为这是一个好主意,切换到内核模式需要时间并且违背了我所问的本质
    【解决方案6】:
    I don't remember from where I copied it, but this code works well for me:
    
            public class QueryPerfCounter
        {
            [DllImport("KERNEL32")]
            private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
            [DllImport("Kernel32.dll")]
            private static extern bool QueryPerformanceFrequency(out long lpFrequency);
        private long start;
        private long stop;
        private long frequency;
        double multiplier = 1.0e6;  // usecs / sec
    
        public QueryPerfCounter()
        {
            if (QueryPerformanceFrequency(out frequency) == false)
            {
                // Frequency not supported
                throw new Win32Exception();
            }
        }
    
        public void Start()
        {
            QueryPerformanceCounter(out start);
        }
    
        public void Stop()
        {
            QueryPerformanceCounter(out stop);
        }
    
        public double Duration(int iterations)
        {
            return ((((stop - start) * multiplier) / frequency) / iterations);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-19
      • 2010-11-27
      • 2010-09-05
      • 1970-01-01
      • 2021-03-11
      • 2018-08-09
      • 2011-07-24
      • 2018-12-15
      相关资源
      最近更新 更多