我正在阅读Agner Fog的优化手册,并且遇到了这个例子:
double data[LEN];
void compute()
{
const double A = 1.1, B = 2.2, C = 3.3;
int i;
for(i=0; i<LEN; i++) {
data[i] = A*i*i + B*i + C;
}
}
Run Code Online (Sandbox Code Playgroud)
Agner 指出,有一种方法可以优化此代码 - 通过认识到循环可以避免使用昂贵的乘法,而是使用每次迭代应用的“增量”。
我用一张纸来证实这个理论,首先......
...当然,他是对的 - 在每次循环迭代中,我们可以通过添加“增量”,基于旧结果计算新结果。该增量从值“A+B”开始,然后每一步增加“2*A”。
所以我们将代码更新为如下所示:
void compute()
{
const double A = 1.1, B = 2.2, C = 3.3;
const double A2 = A+A;
double Z = A+B;
double Y = C;
int i;
for(i=0; i<LEN; i++) {
data[i] …Run Code Online (Sandbox Code Playgroud) 我查看了一些C代码
http://www.mcs.anl.gov/~kazutomo/rdtsc.html
他们使用像" 内联 "," asm "等东西,如下所示:
代码1:
static __inline__ tick gettick (void) {
unsigned a, d;
__asm__ __volatile__("rdtsc": "=a" (a), "=d" (d) );
return (((tick)a) | (((tick)d) << 32));
}
Run Code Online (Sandbox Code Playgroud)
码2:
volatile int __attribute__((noinline)) foo2 (int a0, int a1) {
__asm__ __volatile__ ("");
}
Run Code Online (Sandbox Code Playgroud)
我想知道code1和code2做了什么?
在最近的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) 在尝试构建一个对延迟敏感的应用程序时,需要每秒发送100条消息,每条消息都有时间字段,我们要考虑优化gettimeofday.首先想到的是rdtsc基于优化.有什么想法吗 ?还有其他指针吗?返回的时间值所需的准确度以毫秒为单位,但如果该值偶尔与接收器不同步1-2毫秒,则不是很大.试图比62纳秒的gettimeofday做得更好
在英特尔手册的RDTSC指令警告说,当实际执行RDTSC乱序执行可以改变,所以他们建议将在它前面的CPUID指令,因为CPUID将序列指令流(CPUID是永远不会乱序执行).我的问题很简单:如果他们有能力进行序列化指令,他们为什么不进行RDTSC序列化?它的全部要点似乎是获得周期精确的时间.是否存在一种情况,您不希望在它之前加上序列化指令?
较新的Intel CPU具有单独的序列化RDTSCP指令.英特尔选择引入一个单独的指令,而不是改变RDTSC的行为,这表明我必须存在一些可能出现故障时序的情况.它是什么?
有时我会遇到使用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) 我想在特定点获得CPU周期.我在这一点上使用这个功能:
static __inline__ unsigned long long rdtsc(void)
{
unsigned long long int x;
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
return x;
}
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) c ×5
rdtsc ×5
x86 ×5
x86-64 ×4
assembly ×3
gcc ×2
optimization ×2
performance ×2
c++ ×1
clock ×1
cpu-cycles ×1
cpu-usage ×1
gettimeofday ×1
linux ×1
overhead ×1
simd ×1