使用QueryPerformanceFrequency()和QueryPerformanceCounter()将我的C ++代码转换为C#?

时间:2017-08-07 17:24:17

标签: c#

我一直在将我的游戏从C ++转换为C#。我很好奇计时器是如何使用C#缺少QueryPerformanceFrequency()和QueryPerformanceCounter()的,因为我需要总时间和delta。如何使用我以前的函数将代码转换为C#?

我将逐节介绍这一部分:

我正在使用这个用于C ++的计时器代码:

#include "HPTimer.h"

/// <summary>
/// HP Timer constructor
/// </summary>
HPTimer::HPTimer()
{
    init();
}

/// <summary>
/// Initializes the timer
/// </summary>
void HPTimer::init()
{
    LARGE_INTEGER t;

    QueryPerformanceFrequency(&t);
    m_frequency = t.QuadPart;

    reset();
}

/// <summary>
/// Sets the current time. 
/// </summary>
void HPTimer::reset()
{
    LARGE_INTEGER t;

    QueryPerformanceCounter(&t);

    m_startTime = t.QuadPart;
    m_currentCallToUpdate = t.QuadPart;
    m_lastCallToUpdate = t.QuadPart; 
}

/// <summary>
/// Updates the timer so it's current. 
/// </summary>
void HPTimer::update()
{
    LARGE_INTEGER t;

    m_lastCallToUpdate = m_currentCallToUpdate; 
    QueryPerformanceCounter(&t); 
    m_currentCallToUpdate = t.QuadPart;
}

/// <summary>
/// Gets the total time since reset was called.
/// </summary>
/// <returns>Returns the total time.</returns>
double HPTimer::getTimeTotal()
{
    double d;

    d = m_currentCallToUpdate - m_startTime; 
    return d / m_frequency;
}

/// <summary>
/// Gets the difference between two calls of update. 
/// </summary>
/// <returns>Returns the delta time.</returns>
double HPTimer::getTimeDelta()
{
    double d;

    d = m_currentCallToUpdate - m_lastCallToUpdate;
    return d / m_frequency;
}

Microsoft的解决方案如下: https://msdn.microsoft.com/en-us/library/ff650674.aspx

// QueryPerfCounter.cs
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
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;
  Decimal multiplier = new Decimal(1.0e9);

  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 ((((double)(stop - start)* (double) multiplier) / (double) frequency)/iterations);
  }
}

虽然这看起来很有效,但它缺少我之前使用的一些部分:LARGE_INTEGER,QuadPart,获取总时间,并获得时间增量。在微软的代码中,他们有这个'迭代'的论点,我很困惑。有没有一种方法可以在使用我之前的函数时转换我的代码?

编辑: 我很好奇LARGE_INTEGER / QuadPart。我去了这里: https://msdn.microsoft.com/en-us/library/windows/desktop/aa383713(v=vs.85).aspx 我猜LARGE_INTEGER在C#的世界中确实很长(64位)。所以t.QuadPart被视为out long值,对吗?

更新: 在查看答案后,我进行了以下转换(但仍需要确认)

public class HPTimer
{ 
    private long m_startTime;
    private long m_lastCallUpdate;
    private long m_currentCallToUpdate;
    private long m_frequency;
    private Stopwatch m_stopWatch;

    /// <summary>
    /// HP Timer constructor
    /// </summary>
    public HPTimer()
    {
        Init(); 
    }

    /// <summary>
    /// Initializes the timer
    /// </summary>
    public void Init()
    {
        m_stopWatch = Stopwatch.StartNew(); 
        m_frequency = Stopwatch.Frequency; 

        Reset();
    }

    /// <summary>
    /// Resets the current time. 
    /// </summary> 
    public void Reset()
    {
        m_startTime = Stopwatch.GetTimestamp();
        m_currentCallToUpdate = Stopwatch.GetTimestamp();
        m_lastCallUpdate = Stopwatch.GetTimestamp(); 
    }

    /// <summary>
    /// Updates the timer so it's current. 
    /// </summary>
    public void Update()
    {
        m_lastCallUpdate = m_currentCallToUpdate;
        m_currentCallToUpdate = Stopwatch.GetTimestamp();
    }

    /// <summary>
    /// Gets the total time since reset was called.
    /// </summary>
    /// <returns>Returns the total time.</returns>
    public double GetTotalTime()
    {
        double d;

        d = m_currentCallToUpdate - m_startTime;
        return d / m_frequency;
    }

    /// <summary>
    /// Gets the difference between two calls of update. 
    /// </summary>
    /// <returns>Returns the delta time.</returns>
    public double GetTimeDelta()
    {
        double d;

        d = m_currentCallToUpdate - m_lastCallUpdate;

        return d / m_frequency;
    }
}

2 个答案:

答案 0 :(得分:2)

你有什么理由不能使用Stopwatch吗?它通常会使用高性能计数器(有static field告诉你它是否使用高性能计数器)。

您只需创建一个Stopwatch,然后在每次更新时记录其Elapsed属性。它的类型TimeSpan会超载减法,因此您可以轻松获得增量。

答案 1 :(得分:1)

您可以使用Stopwatch代替非托管Win32 API来记录时间。示例:

//Equivalent to:
//Stopwatch stopWatch = new Stopwatch();
//stopWatch.Start();
Stopwatch stopWatch = Stopwatch.StartNew();

//Operation you want to measure

stopWatch.Stop();

//Timespan is a struct to hold time related info..
//e.g: Days, Hours, Seconds, Milliseconds, Ticks and TotalDays... etc
Timespan ts = stopWatch.Elapsed;

//Or you can simply get the time elapsed in milliseconds like this
long elapsed = stopWatch.ElapsedMilliseconds;


//QueryPerformanceFrequency 
long frequency = Stopwatch.Frequency;

//QueryPerformanceCounter
long ticks = Stopwatch.GetTimestamp();

关于LARGE_INTEGER的其他问题。根据{{​​3}},它的C#等价物应该是:

//Specify struct size and field offsets by ourselves
[StructLayout(LayoutKind.Explicit, Size = 8)]
struct LARGE_INTEGER
{
    [FieldOffset(0)]
    public uint LowPart;
    [FieldOffset(4)]
    public int HighPart;

    [FieldOffset(0)]
    public long QuadPart;
}