从 C# 迁移到 C++,QueryPerformanceCounter 与时钟产生令人困惑的结果
Moving from C# to C++, QueryPerformanceCounter vs clock produce confusing results
在 C# 中,我使用 Stopwatch
class。我可以毫无问题地获取滴答声和毫秒数。
现在我在学习 C++ 的同时测试代码,我尝试进行测量,但是
我不知道匹配 C# 秒表解决方案的等效结果在哪里。我试图搜索,但信息太广泛,我找不到绝对的解决方案。
double PCFreq = 0.0;
__int64 CounterStart = 0;
void StartCounter()
{
LARGE_INTEGER li;
if(!QueryPerformanceFrequency(&li))
std::cout << "QueryPerformanceFrequency failed!\n";
PCFreq = double(li.QuadPart)/1000.0;
QueryPerformanceCounter(&li);
CounterStart = li.QuadPart;
}
double GetCounter()
{
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
return double(li.QuadPart-CounterStart)/PCFreq;
}
因为这给了我两个不同的结果,所以我倾向于相信时钟。 :)
start = StartCounter()
//some function or for loop
end = GetCounter()
marginPc = end - start;
start = clock();
// ...same
end= clock();
marginClck = end - start;
std::cout<< "Res Pc: " << marginPc << "\r\nRes Clck: " marginClck<< std::endl;
对于时钟版本,我尝试了 unsigned int
和 double
,但结果仍然不同。
相当于 C# 秒表的正确方法是什么?
你的错误是这个。你有 StartCounter
return CounterStart = li.QuadPart;
但是GetCounter
returns double(li.QuadPart-CounterStart)/PCFreq
.
即一个除以 PCFreq
而另一个不除。然后从另一个中减去一个是无效的。
clock()
给出程序启动后的毫秒数。例如,下面的程序将打印一个接近 500 的数字:
int main()
{
Sleep(500);
cout << clock() << endl;
/*
POSIX version:
std::cout << clock() * 1000.0 / CLOCKS_PER_SEC << std::endl;
CLOCKS_PER_SEC is 1000 in Windows
*/
return 0;
}
QueryPerformanceCounter
有点类似于GetTickCount64
,它是根据计算机启动的时间来计算的。当你做秒表类型的减法时,结果非常接近。 QueryPerformanceCounter
更准确。来自@BoPersson 的 link 的 chrono
方法也基于 QueryPerformanceCounter
.
MSDN 建议对高分辨率图章使用 QueryPerformanceCounter (QPC):
Acquiring high-resolution time stamps
托管代码中使用了相同的 QPC 函数:
For managed code, the System.Diagnostics.Stopwatch
class uses
QPC as its precise time basis
这个函数应该有合理的精度:
long long getmicroseconds()
{
LARGE_INTEGER fq, t;
QueryPerformanceFrequency(&fq);
QueryPerformanceCounter(&t);
return 1000000 * t.QuadPart / fq.QuadPart;
}
计算机时钟通常每天精确到 +/-1 秒。
自上而下link:
Duration Uncertainty
1 microsecond ± 10 picoseconds (10-12)
1 millisecond ± 10 nanoseconds (10-9)
1 second ± 10 microseconds
1 hour ± 60 microseconds
1 day ± 0.86 seconds
1 week ± 6.08 seconds
为了简化您的其他函数,您可以避免 double
结果。 QuadPart
是 long long
,因此在整个函数中使用它:
long long PCFreq = 0;
long long CounterStart = 0;
void StartCounter()
{
LARGE_INTEGER li;
QueryPerformanceFrequency(&li);
PCFreq = li.QuadPart;
QueryPerformanceCounter(&li);
CounterStart = li.QuadPart;
}
long long GetCounter()
{
if (PCFreq < 1) return 0;
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
//for milliseconds: 1,000
return 1000 * (li.QuadPart - CounterStart) / PCFreq;
//for microseconds: 1,000,000
//return 1000000 * (li.QuadPart - CounterStart) / PCFreq;
}
在 C# 中,我使用 Stopwatch
class。我可以毫无问题地获取滴答声和毫秒数。
现在我在学习 C++ 的同时测试代码,我尝试进行测量,但是 我不知道匹配 C# 秒表解决方案的等效结果在哪里。我试图搜索,但信息太广泛,我找不到绝对的解决方案。
double PCFreq = 0.0;
__int64 CounterStart = 0;
void StartCounter()
{
LARGE_INTEGER li;
if(!QueryPerformanceFrequency(&li))
std::cout << "QueryPerformanceFrequency failed!\n";
PCFreq = double(li.QuadPart)/1000.0;
QueryPerformanceCounter(&li);
CounterStart = li.QuadPart;
}
double GetCounter()
{
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
return double(li.QuadPart-CounterStart)/PCFreq;
}
因为这给了我两个不同的结果,所以我倾向于相信时钟。 :)
start = StartCounter()
//some function or for loop
end = GetCounter()
marginPc = end - start;
start = clock();
// ...same
end= clock();
marginClck = end - start;
std::cout<< "Res Pc: " << marginPc << "\r\nRes Clck: " marginClck<< std::endl;
对于时钟版本,我尝试了 unsigned int
和 double
,但结果仍然不同。
相当于 C# 秒表的正确方法是什么?
你的错误是这个。你有 StartCounter
return CounterStart = li.QuadPart;
但是GetCounter
returns double(li.QuadPart-CounterStart)/PCFreq
.
即一个除以 PCFreq
而另一个不除。然后从另一个中减去一个是无效的。
clock()
给出程序启动后的毫秒数。例如,下面的程序将打印一个接近 500 的数字:
int main()
{
Sleep(500);
cout << clock() << endl;
/*
POSIX version:
std::cout << clock() * 1000.0 / CLOCKS_PER_SEC << std::endl;
CLOCKS_PER_SEC is 1000 in Windows
*/
return 0;
}
QueryPerformanceCounter
有点类似于GetTickCount64
,它是根据计算机启动的时间来计算的。当你做秒表类型的减法时,结果非常接近。 QueryPerformanceCounter
更准确。来自@BoPersson 的 link 的 chrono
方法也基于 QueryPerformanceCounter
.
MSDN 建议对高分辨率图章使用 QueryPerformanceCounter (QPC):
Acquiring high-resolution time stamps
托管代码中使用了相同的 QPC 函数:
For managed code, the
System.Diagnostics.Stopwatch
class uses QPC as its precise time basis
这个函数应该有合理的精度:
long long getmicroseconds()
{
LARGE_INTEGER fq, t;
QueryPerformanceFrequency(&fq);
QueryPerformanceCounter(&t);
return 1000000 * t.QuadPart / fq.QuadPart;
}
计算机时钟通常每天精确到 +/-1 秒。
自上而下link:
Duration Uncertainty
1 microsecond ± 10 picoseconds (10-12)
1 millisecond ± 10 nanoseconds (10-9)
1 second ± 10 microseconds
1 hour ± 60 microseconds
1 day ± 0.86 seconds
1 week ± 6.08 seconds
为了简化您的其他函数,您可以避免 double
结果。 QuadPart
是 long long
,因此在整个函数中使用它:
long long PCFreq = 0;
long long CounterStart = 0;
void StartCounter()
{
LARGE_INTEGER li;
QueryPerformanceFrequency(&li);
PCFreq = li.QuadPart;
QueryPerformanceCounter(&li);
CounterStart = li.QuadPart;
}
long long GetCounter()
{
if (PCFreq < 1) return 0;
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
//for milliseconds: 1,000
return 1000 * (li.QuadPart - CounterStart) / PCFreq;
//for microseconds: 1,000,000
//return 1000000 * (li.QuadPart - CounterStart) / PCFreq;
}