这个新答案使用C ++ 11的<chrono>
功能。虽然还有其他答案说明如何使用<chrono>
,但没有一个答案说明如何<chrono>
与RDTSC
此处其他几个答案中提到的功能一起使用。所以我想我会展示如何使用RDTSC
带<chrono>
。此外,我将演示如何在时钟模板化的测试代码,这样就可以快速切换RDTSC
和系统的内置时钟设施(这将可能是基于clock()
,clock_gettime()
和/或QueryPerformanceCounter
。
请注意,该RDTSC
指令是特定于x86的。 QueryPerformanceCounter
仅Windows。并且clock_gettime()
仅适用于POSIX。下面,我介绍两个新时钟:std::chrono::high_resolution_clock
和std::chrono::system_clock
,如果可以假设使用C ++ 11,它们现在是跨平台的。
首先,这是如何根据英特尔rdtsc
汇编指令创建与C ++ 11兼容的时钟。我称它为x::clock
:
#include <chrono>
namespace x
{
struct clock
{
typedef unsigned long long rep;
typedef std::ratio<1, 2'800'000'000> period; // My machine is 2.8 GHz
typedef std::chrono::duration<rep, period> duration;
typedef std::chrono::time_point<clock> time_point;
static const bool is_steady = true;
static time_point now() noexcept
{
unsigned lo, hi;
asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
return time_point(duration(static_cast<rep>(hi) << 32 | lo));
}
};
} // x
该时钟所做的只是计数CPU周期并将其存储在无符号的64位整数中。您可能需要调整编译器的汇编语言语法。或者您的编译器可能会提供您可以使用的内在函数(例如now() {return __rdtsc();}
)。
要构建时钟,您必须为其提供表示形式(存储类型)。您还必须提供时钟周期,该时钟周期必须是编译时间常数,即使您的机器可能会在不同的电源模式下更改时钟速度。从这些基础上,您可以轻松定义时钟的“本机”持续时间和时间点。
如果您要做的只是输出时钟滴答数,那么您在时钟周期内给出的数字并不重要。仅当您想要将时钟滴答数转换为某些实时单位(例如纳秒)时,此常数才起作用。在这种情况下,您提供时钟速度的能力越精确,到纳秒(毫秒)的转换就越精确。
以下是示例代码,显示了如何使用x::clock
。实际上,我已经在时钟上模板化了代码,因为我想展示如何使用完全相同的语法使用许多不同的时钟。此特定测试显示了在循环下运行要计时的时间时的循环开销:
#include <iostream>
template <class clock>
void
test_empty_loop()
{
// Define real time units
typedef std::chrono::duration<unsigned long long, std::pico> picoseconds;
// or:
// typedef std::chrono::nanoseconds nanoseconds;
// Define double-based unit of clock tick
typedef std::chrono::duration<double, typename clock::period> Cycle;
using std::chrono::duration_cast;
const int N = 100000000;
// Do it
auto t0 = clock::now();
for (int j = 0; j < N; ++j)
asm volatile("");
auto t1 = clock::now();
// Get the clock ticks per iteration
auto ticks_per_iter = Cycle(t1-t0)/N;
std::cout << ticks_per_iter.count() << " clock ticks per iteration\n";
// Convert to real time units
std::cout << duration_cast<picoseconds>(ticks_per_iter).count()
<< "ps per iteration\n";
}
该代码要做的第一件事是创建一个“实时”单位来显示结果。我选择了皮秒,但是您可以选择任何您喜欢的单位,基于整数或浮点。例如,有一个std::chrono::nanoseconds
我可以使用的预制单元。
再举一个例子,我想打印出每次迭代的平均时钟周期数作为浮点数,所以我创建了另一个基于double的持续时间,该持续时间与时钟滴答的单位相同(Cycle
在代码中称为)。
循环的定时是clock::now()
在任一侧调用。如果要命名此函数返回的类型,则为:
typename clock::time_point t0 = clock::now();
(如x::clock
示例中清楚所示,并且系统提供的时钟也是如此)。
要获得一个以浮点时钟滴答为单位的持续时间,只需减去两个时间点,并获得每个迭代值,就将该持续时间除以迭代次数即可。
您可以使用count()
成员函数在任何持续时间内获得计数。这将返回内部表示。最后,我使用std::chrono::duration_cast
将持续时间转换为持续Cycle
时间picoseconds
并打印出来。
使用此代码很简单:
int main()
{
std::cout << "\nUsing rdtsc:\n";
test_empty_loop<x::clock>();
std::cout << "\nUsing std::chrono::high_resolution_clock:\n";
test_empty_loop<std::chrono::high_resolution_clock>();
std::cout << "\nUsing std::chrono::system_clock:\n";
test_empty_loop<std::chrono::system_clock>();
}
在上面,我使用自制的设备进行测试x::clock
,并使用系统提供的两个时钟将这些结果进行比较: std::chrono::high_resolution_clock
和std::chrono::system_clock
。对我来说,它打印出来:
Using rdtsc:
1.72632 clock ticks per iteration
616ps per iteration
Using std::chrono::high_resolution_clock:
0.620105 clock ticks per iteration
620ps per iteration
Using std::chrono::system_clock:
0.00062457 clock ticks per iteration
624ps per iteration
这表明这些时钟中的每个都有不同的滴答周期,因为每个时钟的每次迭代滴答都大不相同。但是,当转换为已知的时间单位(例如皮秒)时,每个时钟得到的结果大致相同(您的里程可能会有所不同)。
请注意,我的代码是如何完全摆脱“魔术转换常数”的。实际上,整个示例中只有两个幻数:
- 为了定义我机器的时钟速度
x::clock
。
- 要测试的迭代次数。如果更改此数字会使结果相差很大,则可能应该增加迭代次数,或者在测试时清空竞争进程的计算机。