【问题标题】:How can I synchronize TSC across cores?如何跨内核同步 TSC?
【发布时间】:2012-05-07 15:20:17
【问题描述】:

使用:

inline uint64_t rdtsc()
{
  uint32_t cycles_high;
  uint32_t cycles_low;

  asm volatile ("CPUID\n\t"
      "RDTSC\n\t"
      "mov %%edx, %0\n\t"
      "mov %%eax, %1\n\t": "=r" (cycles_high), "=r" (cycles_low)::
      "%rax", "%rbx", "%rcx", "%rdx");
  return ( ((uint64_t)cycles_high << 32) | cycles_low );
}

线程 1 正在运行

while(globalIndex < COUNT)
{
  while(globalIndex %2 == 0 && globalIndex < COUNT)
    ;
  cycles[globalIndex][0] = rdtsc();
  cycles[globalIndex][1] = cpuToBindTo;
  __sync_add_and_fetch(&globalIndex,1);
}

线程 2 正在运行

while(globalIndex < COUNT)
{
  while(globalIndex %2 == 1 && globalIndex < COUNT)
    ;
  cycles[globalIndex][0] = rdtsc();
  cycles[globalIndex][1] = cpuToBindTo;
  __sync_add_and_fetch(&globalIndex,1);
}

我看到了

CPU     rdtsc()         t1-t0
11 = 5023231563212740   990
03 = 5023231563213730   310
11 = 5023231563214040   990
03 = 5023231563215030   310
11 = 5023231563215340   990
03 = 5023231563216330   310
11 = 5023231563216640   990
03 = 5023231563217630   310
11 = 5023231563217940   990
03 = 5023231563218930   310
11 = 5023231563219240   990
03 = 5023231563220230   310
11 = 5023231563220540   990
03 = 5023231563221530   310
11 = 5023231563221840   990
03 = 5023231563222830   310
11 = 5023231563223140   990
03 = 5023231563224130   310
11 = 5023231563224440   990
03 = 5023231563225430   310
11 = 5023231563225740   990
03 = 5023231561739842   310
11 = 5023231561740152   990
03 = 5023231561741142   310
11 = 5023231561741452   12458
03 = 5023231561753910   458
11 = 5023231561754368   1154
03 = 5023231561755522   318
11 = 5023231561755840   982
03 = 5023231561756822   310
11 = 5023231561757132   990
03 = 5023231561758122   310
11 = 5023231561758432   990
03 = 5023231561759422   310

我不确定我是如何收到 12458 的乒乓球的,但想知道为什么我看到的是 310-990-310 而不是 650-650-650。我认为 tsc 应该是跨核心同步的。我的 constant_tsc cpu 标志已打开。

【问题讨论】:

    标签: c++ linux performance assembly


    【解决方案1】:

    您在什么上运行此代码? TSC 同步应该在操作系统/内核中完成,并且依赖于硬件。例如,您可以通过引导加载程序将powernow-k8.tscsync=1 之类的标志传递给内核引导参数。

    您需要为您的操作系统和硬件组合搜索正确的 TSC 同步方法。总的来说,这整个过程是自动化的——如果您在自定义内核或非 i686 硬件上运行,我不会感到惊讶?

    如果您在 Google 上使用正确的字词进行搜索,您会发现很多资源,例如关于此主题的邮件列表讨论。例如,这里是one algorithm being discussed(尽管它显然不是一个好方法)。然而,这不是用户级开发人员应该担心的事情——这是只有内核开发人员才需要担心的神秘魔法。

    基本上,操作系统的工作是在引导时同步 SMP 机器上所有不同处理器和/或内核之间的 TSC 计数器,并在一定的误差范围内。如果您看到的数字非常偏离,则 TSC 同步存在问题,您最好花时间找出您的操作系统未正确同步 TSC 的原因,而不是尝试实施您自己的 TSC 同步算法。

    【讨论】:

      【解决方案2】:

      您有 NUMA 内存架构吗?全局计数器可以位于 RAM 中,其中一个 CPU 距离几跳,另一个 CPU 位于本地。您可以通过将线程固定到同一 NUMA 节点上的核心来测试这一点。

      编辑:我猜这是因为性能是特定于 CPU 的。

      编辑:关于同步 TSC。我不知道一种简单的方法,这并不是说没有!如果您将核心 1 作为参考时钟,然后将其与核心 2 进行比较,会发生什么情况?如果您多次进行该比较并取最小值,您可能会得到一个很好的近似值。当您在比较过程中被抢占时,这应该可以处理这种情况。

      【讨论】:

      • 我在一个 2 插槽服务器上使用 cpus 3 和 11 运行它,它们位于套接字 2 上。 lscpu | grep NUMA 返回 ' NUMA node0 CPU(s): 0,2,4,6,8,10 NUMA node1 CPU(s): 1,3,5,7,9,11 '
      • 只是为了它......当你在 3 和 11 以外的不同 CPU 上运行线程时会发生什么?
      • 全局索引是否易变?虽然,对于全局变量,volatile 可能不是必需的。你用-O3编译吗?此外,为了获得更清晰的统计信息,我们可以在不污染其他线程的缓存行的数组中汇总统计信息。使用一个结构来保存 CPU/时间,然后添加几个虚假字段,因此它加起来最多 64 个字节。然后用 __attribute((align(64)) 声明循环。之后每次更新都应该在一个干净的缓存行上。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-03
      • 2016-05-09
      • 1970-01-01
      • 2015-06-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多