标签: rdtsc

rdtsc,周期太多了

#include <stdio.h>
static inline unsigned long long tick() 
{
        unsigned long long d;
        __asm__ __volatile__ ("rdtsc" : "=A" (d) );
        return d;
}

int main()
{
        long long res;
        res=tick();

        res=tick()-res;
        printf("%d",res);
        return 0;
}
Run Code Online (Sandbox Code Playgroud)

我用gcc编译了这段代码,并使用了-O0 -O1 -O2 -O3优化.我总是得到2000-2500个周期.任何人都可以解释这个输出的原因吗?如何度过这些周期?

第一个函数"tick"是错误的.这是对的.

另一个版本的功能"滴答"

static __inline__ unsigned long long tick()
{
  unsigned hi, lo;
  __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
  return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
Run Code Online (Sandbox Code Playgroud)

这是-O3的汇编代码

 .file  "rdtsc.c"
.section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%d"
    .text
    .p2align 4,,15
.globl …
Run Code Online (Sandbox Code Playgroud)

c x86 assembly rdtsc

8
推荐指数
3
解决办法
7630
查看次数

英特尔的时间戳读取asm代码示例是否使用了两个以上的寄存器?

我正在研究使用x86 CPU中的时间戳寄存器(TSR)来测量基准性能.它是一个有用的寄存器,因为它以单调时间单位测量,不受时钟速度变化的影响.很酷.

这是一份英特尔文档,显示了使用TSR进行可靠基准测试的asm片段,包括使用cpuid进行管道同步.见第16页:

http://www.intel.com/content/www/us/en/embedded/training/ia-32-ia-64-benchmark-code-execution-paper.html

要读取开始时间,它说(我注释了一下):

__asm volatile (
    "cpuid\n\t"             // writes e[abcd]x
    "rdtsc\n\t"             // writes edx, eax
    "mov %%edx, %0\n\t" 
    "mov %%eax, %1\n\t"
    //
    :"=r" (cycles_high), "=r" (cycles_low)  // outputs
    :                                       // inputs
    :"%rax", "%rbx", "%rcx", "%rdx");       // clobber
Run Code Online (Sandbox Code Playgroud)

我不知道为什么暂存寄存器用来取的价值观edxeax.为什么不删除MOVS和读取TSR值右出的edxeax?像这样:

__asm volatile(                                                             
    "cpuid\n\t"
    "rdtsc\n\t"
    //
    : "=d" (cycles_high), "=a" (cycles_low) // outputs
    :                                       // inputs
    : "%rbx", "%rcx");                      // clobber     
Run Code Online (Sandbox Code Playgroud)

通过这样做,您可以保存两个寄存器,从而降低C编译器需要溢出的可能性.

我对吗?或者那些MOV在某种程度上是战略性的?

(我同意你确实需要临时寄存器来读取停止时间,因为在那种情况下指令的顺序是相反的:你有rdtscp,...,cpuid.cpuid指令破坏了rdtscp的结果).

谢谢

c benchmarking assembly inline-assembly rdtsc

8
推荐指数
1
解决办法
803
查看次数

确定Linux上的TSC频率

给定具有恒定TSC的x86 ,这对于测量实时非常有用,如何在启动时使用Linux计算的TSC校准因子在TSC参考周期的"单位"和正常人类实时单位(如纳秒)之间进行转换

也就是说,当然可以通过CLOCK_MONOTONIC在某个时间间隔的两端进行TSC和时钟测量(例如,用)来计算用户区中的TSC频率,以确定TSC频率,但Linux已经在启动时进行了此计算,因为它内部使用TSC帮助进行计时.

例如,您可以通过以下方式查看内核的结果dmesg | grep tsc:

[    0.000000] tsc: PIT calibration matches HPET. 2 loops
[    0.000000] tsc: Detected 3191.922 MHz processor
[    1.733060] tsc: Refined TSC clocksource calibration: 3192.007 MHz
Run Code Online (Sandbox Code Playgroud)

在更糟糕的情况下,我猜你可以尝试dmesg在运行时grep结果,但坦率地看起来很可怕,脆弱和各种各样的坏0.

使用内核确定的校准时间的优点很多:

  1. 您不必自己编写TSC校准例程,并且您可以非常确定Linux版本是最佳的.
  2. 当你使用现有的二进制文件(例如,最近芯片开始使用cpuid叶片0x15 宣传其TSC频率,因此不一定需要进行校准)时,您会自动在TSC校准中获取新技术.
  3. 您不会通过TSC校准减慢启动速度.
  4. 您在每次运行过程中使用相同的TSC值(至少在重新启动之前).
  5. 您的TSC频率与OS时间保持功能(如1gettimeofdayclock_gettime1)使用的TSC频率在某种程度上"一致" .
  6. 内核能够在引导时,在内核模式下很早地进行TSC校准,没有中断的祸害,其他进程,并且能够访问底层硬件定时器方向作为其校准源.

然而,使用Linux的TSC校准的一些缺点包括:

  1. 它不适用于每个Linux安装(例如,可能不使用tsc clocksource的安装)或其他操作系统上的安装,因此您可能仍然无法编写后备校准方法.
  2. 有理由相信"最近"校准可能比旧校准更准确,特别是在开机后立即进行校准:晶体行为可能会发生变化,特别是在温度变化时,因此通过这样做可以获得更准确的频率手动接近您将使用它的点.

0例如:系统可能没有dmesg安装,您可能无法以普通用户身份运行它,累积的输出可能已经缠绕,因此线路不再存在,您可能会在grep上获得误报,内核消息是英文散文,可能会有变化,可能很难启动子流程等等.

1这是否有争议 - 但如果您将rdtsc调用与使用OS时间保持的代码混合,则可能会提高精度.

linux performance x86 rdtsc

8
推荐指数
0
解决办法
961
查看次数

什么相当于PPC的rdtsc操作码?

我有一个具有以下代码的汇编程序.此代码可编译为intel处理器.但是,当我使用PPC(交叉)编译器时,我收到一个错误,即操作码无法识别.我试图找出PPC架构是否有等效的操作码.

.file   "assembly.s"
.text
.globl func64
.type   func64,@function
func64:
    rdtsc
    ret

.size   func64,.Lfe1-func64
.globl func
.type   func,@function
func:
    rdtsc
    ret
Run Code Online (Sandbox Code Playgroud)

powerpc opcode rdtsc

7
推荐指数
1
解决办法
1916
查看次数

如何计算CPU核心的频率

我试图使用RDTSC,但似乎我的方法可能是错误的获得核心速度:

#include "stdafx.h"
#include <windows.h>
#include <process.h>
#include <iostream>

using namespace std;

struct Core
{
    int CoreNumber;
};

static void startMonitoringCoreSpeeds(void *param)
{
    Core core = *((Core *)param);
    SetThreadAffinityMask(GetCurrentThread(), 1 << core.CoreNumber);
    while (true)
    {
        DWORD64 first = __rdtsc();
        Sleep(1000);
        DWORD64 second = __rdtsc();
        cout << "Core " << core.CoreNumber << " has frequency " << ((second - first)*pow(10, -6)) << " MHz" << endl;
    }
}

int GetNumberOfProcessorCores()
{
    DWORD process, system;
    if (GetProcessAffinityMask(GetCurrentProcess(), &process, &system))
    {
        int count …
Run Code Online (Sandbox Code Playgroud)

c++ performance winapi visual-c++ rdtsc

7
推荐指数
1
解决办法
2643
查看次数

为什么RDTSC是现代处理器的虚拟化指令?

我正在研究RDTSC,并了解它是如何虚拟化虚拟机,如VirtualBox和VMWare.为什么Intel/AMD会遇到虚拟化此指令的麻烦?

我觉得它可以很容易地用陷阱来模拟它并不是一个超常用的指令(我测试过并且在禁用硬件RDTSC虚拟化的虚拟机中一般用法没有明显的减速).

但是,我知道英特尔/ AMD不会把这个指令添加到虚拟化硬件中,除非能够非常快速地执行它是很重要的.

有谁知道为什么?

hardware virtualization rdtsc

5
推荐指数
1
解决办法
3731
查看次数

rdtscp 的“半围栏”行为是怎么回事?

多年来,x86 CPU 都支持该rdtsc指令,该指令读取当前 CPU 的“时间戳计数器”。这个计数器的确切定义随着时间的推移而改变,但在最近的 CPU 上,它是一个相对于挂钟时间以固定频率递增的计数器,因此它作为快速、准确时钟或测量时间的构建块非常有用由小段代码获取。

关于rdtsc指令的一个重要事实没有以任何特殊方式与周围的代码一起排序。像大多数指令一样,它可以相对于与它没有依赖关系的其他指令自由地重新排序。这实际上是“正常的”,对于大多数指令,它只是一种使 CPU 更快的几乎不可见的方式(这只是一种长篇大论的无序执行方式)。

因为rdtsc它很重要,因为这意味着您可能没有为您期望的代码计时。例如,给定以下序列1

rdtsc
mov ecx, eax
mov rdi, [rdi]
mov rdi, [rdi]
rdtsc
Run Code Online (Sandbox Code Playgroud)

您可能希望rdtsc测量追逐加载负载的两个指针的延迟mov rdi, [rdi]。然而,在实践中,即使这两个加载都需要查看时间(如果它们在缓存中丢失,则为 100 秒),您将获得相当小的读取值rdtsc。问题是第二个rdtsc不等待加载完成,它只是乱序执行,所以你没有按你认为的时间间隔计时。也许这两rdtsc条指令实际上甚至在第一次加载开始之前就执行了,这取决于rdi在此示例之前的代码中是如何计算的。

到目前为止,这听起来更像是对一个没人问的问题的回答,而不是一个真正的问题,但我已经到了那里。

您有两个基本用例rdtsc

  • 作为一个快速时间戳,您通常可以不关心它如何与周围代码重新排序,因为无论如何您可能没有关于时间戳应该在哪里使用的指令级概念。
  • 作为一种精确的计时机制,例如,在微基准测试中。在这种情况下,您通常会rdtsc根据lfence说明防止重新订购。对于上面的示例,您可能会执行以下操作:

    lfence
    rdtsc
    lfence
    mov ecx, eax
    ...
    lfence
    rdtsc
    
    Run Code Online (Sandbox Code Playgroud)

    确保定时指令 ( ...) 不会逃逸到定时区域之外,并确保来自时间区域内的指令不会进入(可能问题不大,但它们可能会与您想要的代码竞争资源测量)。

多年后,英特尔看不起我们这些可怜的程序员,并提出了一条新指令:rdtscp. 就像rdtsc它返回时间戳计数器的读数一样,这家伙做了更多的事情:它使用时间戳读数原子地读取特定于内核的 MSR …

performance x86 assembly microbenchmark rdtsc

5
推荐指数
0
解决办法
301
查看次数

为什么 CPUID + RDTSC 不可靠?

我正在尝试在 x86-64 处理器上分析执行时间的代码。我指的是这篇英特尔白皮书,并且还浏览了其他 SO 线程,讨论了在此处此处使用 RDTSCP 与 CPUID+RDTSC 的主题。

在上面提到的白皮书中,使用 CPUID+RDTSC 的方法被称为不可靠,并且也使用统计数据进行了证明。

CPUID+RDTSC 不可靠的原因可能是什么?

此外,同一白皮书中的图 1(最小值行为图)和图 2(方差行为图)中的图具有“方波”模式。什么解释了这种模式?

x86 intel microbenchmark rdtsc cpuid

5
推荐指数
1
解决办法
531
查看次数

Windows 中优于 100 纳秒分辨率的计时器

我从事编程语言分析器工作,正在寻找分辨率优于 100 ns 的 Windows 计时器解决方案。

  • QueryPerformanceCounter应该是一个答案,但在 Windows 10 上返回的频率为QueryPerformanceFrequency10 MHz,在 Windows 7 上甚至更低

  • GetSystemTimePreciseAsFileTime有 100 ns 刻度/步

  • RDTSC分辨率优于1ns,但随频率变化

我的目标分辨率至少为 10 ns。

目前最好的解决方案是什么?

如何QueryPerformanceCounter实施?是否可以轻松拆卸并提高分辨率?

是否可以RDTSC在每次频率变化时直接使用并跟踪/中断?

c c++ benchmarking timing rdtsc

5
推荐指数
1
解决办法
1733
查看次数

Clang 优化 RDTSC asm 块,认为重复的块产生与前一个块相同的结果。这合法吗?

假设我们有一些相同的汇编的重复,其中包含RDTSC诸如

    volatile size_t tick1;
    asm ( "rdtsc\n"           // Returns the time in EDX:EAX.
          "shl $32, %%rdx\n"  // Shift the upper bits left.
          "or %%rdx, %q0"     // 'Or' in the lower bits.
          : "=a" (tick1)
          : 
          : "rdx");
    
    this_thread::sleep_for(1s);

    volatile size_t tick2;    
    asm ( "rdtsc\n"          // clang's optimizer just thinks this asm yields
          "shl $32, %%rdx\n" // the same bits as above, so it just loads
          "or %%rdx, %q0"    // the result to qword ptr [rsp + 8]
          : …
Run Code Online (Sandbox Code Playgroud)

c++ x86 gcc inline-assembly rdtsc

5
推荐指数
1
解决办法
158
查看次数