【问题标题】:QueryPerformance counter and Queryperformance frequency in windowswindows中查询性能计数器和查询性能频率
【发布时间】:2013-11-11 09:32:14
【问题描述】:
#include <windows.h>
#include <stdio.h>
#include <stdint.h>

// assuming we return times with microsecond resolution
#define STOPWATCH_TICKS_PER_US  1

uint64_t GetStopWatch()
{
    LARGE_INTEGER t, freq;
    uint64_t val;

    QueryPerformanceCounter(&t);
    QueryPerformanceFrequency(&freq);
    return (uint64_t) (t.QuadPart / (double) freq.QuadPart * 1000000);
}

void task()
{
    printf("hi\n");
}

int main()
{
  uint64_t start = GetStopWatch();
  task();
  uint64_t stop = GetStopWatch();

  printf("Elapsed time (microseconds): %lld\n", stop - start);
}

上面包含一个查询性能计数器函数检索高分辨率性能计数器的当前值和查询性能频率函数检索高分辨率性能计数器的频率。如果我正在调用任务();函数多次,然后开始和停止时间之间的差异会有所不同,但我应该得到相同的时间差来多次调用任务函数。谁能帮我找出上面代码中的错误?

【问题讨论】:

  • 这甚至不是你的代码。代码由 Ale here 提供。
  • Ale 给了我上面的代码并尝试用我的应用程序进行修改,但我多次调用任务函数的时间有所不同。

标签: c windows timer task performancecounter


【解决方案1】:

问题是,Windows 是一个先发制人的多任务操作系统。你问这到底是什么意思?

'Simple' - windows 为系统中每个正在运行的进程分配时间片。这会产生数十或数百个进程并行运行的错觉。实际上,在典型的台式机/笔记本电脑中,您仅限于 2、4、8 或 16 个并行进程。 Intel i3 有 2 个物理内核,每个内核都可以给人一种同时做两件事的印象。 (但实际上,有一些硬件技巧可以在每个内核可以同时处理的两个线程之间切换执行)这是对 Windows/Linux/MacOSX 所做的软件上下文切换的补充。

这些时间片不保证每次都具有相同的持续时间。您可能会发现 pc 与 windows.time 同步以更新您的时钟,您可能会发现病毒扫描程序决定开始工作,或其他任何事情。所有这些事件都可能发生在您的 task() 函数开始之后,但在它结束之前。

在 DOS 时代,每次对 task() 的单次迭代计时,都会得到几乎相同的结果。不过,多亏了 TSR 程序,您仍然可以发现一个中断被触发,并且在执行期间一些机器时间被盗。

正是由于这些原因,可以通过将任务运行 N 次,将经过的时间除以 N 来获得每次迭代的时间,从而更准确地确定任务执行所需的时间。

对于过去的某些函数,我使用的 N 值高达 1 亿。

编辑:简短的 sn-p。

LARGE_INTEGER tStart, tEnd;
LARGE_INTEGER tFreq;
double tSecsElapsed;
QueryPerformanceFrequency(&tFreq);
QueryPerformanceCounter(&tStart);

int i, n = 100;
for (i=0; i<n; i++)
{
// Do Something
}

QueryPerformanceCounter(&tEnd);
tSecsElapsed = (tEnd.QuadPart - tStart.QuadPart) / (double)tFreq.QuadPart;
double tMsElapsed = tSecElapsed * 1000;

double tMsPerIteration = tMsElapsed / (double)n;

【讨论】:

  • 你能帮我修改一下上面的代码吗??
  • 是的,我有。我告诉你你需要做什么,所以去做吧。复制/粘贴编码员需要被殴打和排斥,而不是用勺子喂!如果你还懒得想,我给你这个。将对 task() 的调用放在具有 N 次迭代的 for 循环中。完成后,用iterationTime = (stopTime-startTime)/N; 计算迭代时间(是的,我心情不好,抱歉,这就是你问愚蠢的问题而没有谢谢你的结果!)
  • 非常感谢!!!我知道如何在上面的代码中多次调用任务函数。我的问题:我多次调用任务函数,每次都打印 hi(上面的代码中没有显示)。为什么每次显示不同的经过时间??
  • 不客气。我真的看不到你的屏幕,你知道,这有点难以推测......我想知道你的数学运算的顺序是否导致溢出。我现在太累了,无法进行桌面检查。但是我提供了一些我使用的代码,它们在 1 亿次迭代中运行良好(即 n = 100000000)。您会在我更新的原始解决方案中找到代码。 :)
  • #include "TIMER1.h" #include "MAIN.h" typedef unsigned int uint16_t; uint16_t 计数; /** * 此任务每 2ms 激活一次。 */ void TASK1( ) { LARGE_INTEGER 开始、停止、频率;双倍时间_len;查询性能频率(&freq); QueryPerformanceCounter(&start); printf("你好\n"); QueryPerformanceCounter(&stop); time_len = (stop.QuadPart - start.QuadPart) / (double) freq.QuadPart; printf("每次迭代的时间:%0.8f 秒。\n", time_len); }
【解决方案2】:

现代操作系统和处理器上的代码执行时间非常无法预测。在任何情况下,您都无法确定经过的时间实际测量了代码所花费的时间,您的程序很可能在执行时将处理器丢给了另一个进程。处理器使用的缓存起着很大的作用,当缓存中还没有包含程序使用的代码和数据时,第一次执行代码时总是会慢很多。与处理器相比,内存总线非常慢。

当您测量 printf() 语句时,它变得特别没有意义。控制台窗口由另一个进程拥有,因此有很大一部分进程互操作开销,其执行时间严重取决于该进程的状态。例如,当需要滚动控制台窗口时,您会突然看到巨大的差异。最重要的是,实际上你无法做任何事情来让它更快,所以测量它只是为了好奇。

您可以改进的仅配置文件代码。采集许多样本,这样您就可以摆脱异常值。永远不要选择最低的测量值,这只会产生不切实际的期望。也不要选择平均值,这会受到其他进程在您的测试中可能产生的长时间延迟的很大影响。 中值是一个不错的选择。

【讨论】:

  • 获得我的投票,指出中值具有最大的效用 - 在其他三个之后,我试图给出一个清晰、简洁和全面的答案。感谢您有机会改进我的矿井。
  • @Hans Pasant :非常感谢您的回复:您能告诉我中间值是多少吗??
  • 对样本进行排序,取中间的一个。显而易见的谷歌查询是“中值”,热门点击都很好。
猜你喜欢
  • 2012-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-05
  • 1970-01-01
  • 1970-01-01
  • 2010-12-16
  • 1970-01-01
相关资源
最近更新 更多