假设我们正在尝试使用tsc进行性能监控,我们希望防止指令重新排序.
这些是我们的选择:
1: rdtscp是序列化调用.它可以防止对rdtscp的调用进行重新排序.
__asm__ __volatile__("rdtscp; " // serializing read of tsc
"shl $32,%%rdx; " // shift higher 32 bits stored in rdx up
"or %%rdx,%%rax" // and or onto rax
: "=a"(tsc) // output to tsc variable
:
: "%rcx", "%rdx"); // rcx and rdx are clobbered
Run Code Online (Sandbox Code Playgroud)
但是,rdtscp仅适用于较新的CPU.所以在这种情况下我们必须使用rdtsc.但是rdtsc非序列化,因此单独使用它不会阻止CPU重新排序.
所以我们可以使用这两个选项中的任何一个来防止重新排序:
2:这是一个电话cpuid然后rdtsc.cpuid是一个序列化的电话.
volatile int dont_remove __attribute__((unused)); // volatile to stop optimizing
unsigned tmp;
__cpuid(0, tmp, tmp, tmp, …Run Code Online (Sandbox Code Playgroud) 我正在从一个线程发送网络数据包,并在另一个运行在不同CPU核心上的线程上接收回复.我的进程测量每个数据包的发送和接收之间的时间(类似于ping).我正在使用rdtsc来获得高分辨率,低开销的时序,这是我的实现所需要的.
所有测量看起来都很可靠.尽管如此,我仍然担心核心的rdtsc准确性,因为我一直在阅读一些暗示tsc未在核心之间同步的文本.
恒定的TSC行为可确保每个时钟周期的持续时间均匀,并支持将TSC用作挂钟定时器,即使处理器内核更改频率也是如此.这是所有英特尔处理器的架构行为.
我仍然担心核心的累积性,这是我的问题
在最近的CPU上(至少在过去十年左右),除了各种可配置的性能计数器之外,英特尔还提供了三个固定功能硬件性能计数器.三个固定柜台是:
INST_RETIRED.ANY
CPU_CLK_UNHALTED.THREAD
CPU_CLK_UNHALTED.REF_TSC
Run Code Online (Sandbox Code Playgroud)
第一个计算退役指令,第二个计算实际周期,最后一个是我们感兴趣的."英特尔软件开发人员手册"第3卷的描述如下:
当核心未处于暂停状态而不处于TM停止时钟状态时,此事件计算TSC速率下的参考周期数.核心在运行HLT指令或MWAIT指令时进入暂停状态.此事件不受核心频率变化(例如,P状态)的影响,但计数与时间戳计数器的频率相同.当核心未处于暂停状态而不处于TM stopclock状态时,此事件可以估计经过的时间.
因此,对于CPU绑定循环,我希望该值与从中读取的自由运行TSC值相同rdstc,因为它们应该仅针对暂停的循环指令或"TM stopclock state"是什么发散.
我使用以下循环测试它(整个独立演示在github上可用):
for (int i = 0; i < 100; i++) {
PFC_CNT cnt[7] = {};
int64_t start = nanos();
PFCSTART(cnt);
int64_t tsc =__rdtsc();
busy_loop(CALIBRATION_LOOPS);
PFCEND(cnt);
int64_t tsc_delta = __rdtsc() - tsc;
int64_t nanos_delta = nanos() - start;
printf(CPU_W "d" REF_W ".2f" TSC_W ".2f" MHZ_W ".2f" RAT_W ".6f\n",
sched_getcpu(),
1000.0 * cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC] / nanos_delta,
1000.0 * tsc_delta / nanos_delta,
1000.0 * CALIBRATION_LOOPS / nanos_delta,
1.0 * cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC]/tsc_delta); …Run Code Online (Sandbox Code Playgroud) 我在SO上看到这篇文章,其中包含C代码以获取最新的CPU周期数:
基于CPU周期计算的C/C++ Linux x86_64中的分析
有没有办法在C++中使用这个代码(欢迎使用windows和linux解决方案)?虽然用C语言编写(而C是C++的一个子集)但我不太确定这段代码是否适用于C++项目,如果没有,如何翻译呢?
我使用的是x86-64
EDIT2:
找到此功能但无法让VS2010识别汇编程序.我需要包含任何内容吗?(我相信我必须换uint64_t到long long窗户......?)
static inline uint64_t get_cycles()
{
uint64_t t;
__asm volatile ("rdtsc" : "=A"(t));
return t;
}
Run Code Online (Sandbox Code Playgroud)
EDIT3:
从上面的代码我得到错误:
"错误C2400:'操作码'中的内联汇编语法错误;找到'数据类型'"
有人可以帮忙吗?
有时我会遇到使用rdtsc指令读取TSC的代码,但cpuid之前会调用.
为什么要打电话cpuid?我意识到这可能是与有TSC的值不同的内核,但什么究竟,当你调用序列这两个指令会发生什么?
我正在编写一个C代码,用于测量获取信号量所需的时钟周期数.我正在使用rdtsc,在对信号量进行测量之前,我连续两次调用rdtsc来测量开销.我在for循环中重复了这么多次,然后我将平均值用作rdtsc开销.
这是正确的,首先要使用平均值吗?
尽管如此,这里的一个大问题是,有时我会得到开销的负值(不一定是平均值,但至少是for循环中的部分值).
这也影响了连续计算sem_wait()操作所需的cpu周期数,有时也证明是负数.如果我写的不清楚,这里有一部分我正在编写的代码.
为什么我会得到这样的负值?
(编者注:请参阅获取CPU周期计数?以获得完整的64位时间戳的正确和可移植方式."=A"编译为x86-64时,asm约束只能得到低或高32位,具体取决于寄存器分配是否发生为uint64_t输出选择RAX或RDX .它不会选择edx:eax.)
(编辑的第二个注释:哎呀,这就是为什么我们得到负面结果的答案.仍然值得留下一个注释作为警告,不要复制这个rdtsc实现.)
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
static inline uint64_t get_cycles()
{
uint64_t t;
// editor's note: "=A" is unsafe for this in x86-64
__asm volatile ("rdtsc" : "=A"(t));
return t;
}
int num_measures = 10;
int main ()
{
int i, value, res1, res2;
uint64_t c1, c2;
int tsccost, tot, a;
tot=0;
for(i=0; i<num_measures; i++)
{ …Run Code Online (Sandbox Code Playgroud) 我知道rdtsc将处理器的时间戳计数器的当前值加载到两个寄存器中:EDX和EAX.为了在x86上获取它,我需要这样做(假设使用Linux):
unsigned long lo, hi;
asm( "rdtsc" : "=a" (lo), "=d" (hi));
return lo;
Run Code Online (Sandbox Code Playgroud)
对于x86_x64:
unsigned long lo, hi;
asm( "rdtsc" : "=a" (lo), "=d" (hi) );
return( lo | (hi << 32) );
Run Code Online (Sandbox Code Playgroud)
这是为什么?任何人都可以向我解释一下吗?
使用Visual Studio,我可以从处理器读取时钟周期计数,如下所示.我如何与GCC做同样的事情?
#ifdef _MSC_VER // Compiler: Microsoft Visual Studio
#ifdef _M_IX86 // Processor: x86
inline uint64_t clockCycleCount()
{
uint64_t c;
__asm {
cpuid // serialize processor
rdtsc // read time stamp counter
mov dword ptr [c + 0], eax
mov dword ptr [c + 4], edx
}
return c;
}
#elif defined(_M_X64) // Processor: x64
extern "C" unsigned __int64 __rdtsc();
#pragma intrinsic(__rdtsc)
inline uint64_t clockCycleCount()
{
return __rdtsc();
}
#endif
#endif
Run Code Online (Sandbox Code Playgroud) 我正在构建一个微基准来测量性能变化,因为我在一些原始图像处理操作中尝试使用SIMD指令内在函数.但是,编写有用的微基准测试很困难,因此我想首先了解(如果可能的话)消除尽可能多的变异和误差源.
我必须考虑的一个因素是测量代码本身的开销.我正在使用RDTSC进行测量,我正在使用以下代码来查找测量开销:
extern inline unsigned long long __attribute__((always_inline)) rdtsc64() {
unsigned int hi, lo;
__asm__ __volatile__(
"xorl %%eax, %%eax\n\t"
"cpuid\n\t"
"rdtsc"
: "=a"(lo), "=d"(hi)
: /* no inputs */
: "rbx", "rcx");
return ((unsigned long long)hi << 32ull) | (unsigned long long)lo;
}
unsigned int find_rdtsc_overhead() {
const int trials = 1000000;
std::vector<unsigned long long> times;
times.resize(trials, 0.0);
for (int i = 0; i < trials; ++i) {
unsigned long long t_begin = rdtsc64();
unsigned long long t_end = rdtsc64(); …Run Code Online (Sandbox Code Playgroud) 我在constant_tsc和cpu上运行这个测试nonstop_tsc
$ grep -m 1 ^flags /proc/cpuinfo | sed 's/ /\n/g' | egrep "constant_tsc|nonstop_tsc"
constant_tsc
nonstop_tsc
Run Code Online (Sandbox Code Playgroud)
第1步:计算tsc的滴答率:
我计算_ticks_per_ns了许多观测值的中位数.我用它rdtscp来确保按顺序执行.
static const int trials = 13;
std::array<double, trials> rates;
for (int i = 0; i < trials; ++i)
{
timespec beg_ts, end_ts;
uint64_t beg_tsc, end_tsc;
clock_gettime(CLOCK_MONOTONIC, &beg_ts);
beg_tsc = rdtscp();
uint64_t elapsed_ns;
do
{
clock_gettime(CLOCK_MONOTONIC, &end_ts);
end_tsc = rdtscp();
elapsed_ns = to_ns(end_ts - beg_ts); // calculates ns between two timespecs
}
while (elapsed_ns < …Run Code Online (Sandbox Code Playgroud)