c++ - RDTSCP 是否跨多核单调递增?
问题描述
我很困惑rdtscp
在多核环境中是否单调递增。根据文档:__rdtscp,rdtscp
似乎是基于处理器的指令,可以防止在调用周围重新排序指令。
处理器在每个时钟周期单调递增时间戳计数器 MSR,并在处理器复位时将其复位为 0。
rdtscp
肯定会在同一个 CPU 内核上单调递增,但是这个rdtscp
时间戳是否保证跨不同 CPU 内核单调?我相信没有这样的绝对保证。例如,
Thread on CPU core#0 Thread on CPU core#1
unsigned int ui;
uint64_t t11 = __rdtscp(&ui);
uint64_t t12 = __rdtscp(&ui);
uint64_t t13 = __rdtscp(&ui);
unsigned int ui;
uint64_t t21 = __rdtscp(&ui);
uint64_t t22 = __rdtscp(&ui);
uint64_t t23 = __rdtscp(&ui);
以我的理解,我们可以有一个决定性的结论t13 > t12 > t11
,但我们不能保证t21 > t13
。
我想写一个脚本来测试我的理解是否正确,但我不知道如何构造一个例子来验证我的假设。
// file name: rdtscptest.cpp
// g++ rdtscptest.cpp -g -lpthread -Wall -O0 -o run
#include <chrono>
#include <thread>
#include <iostream>
#include <string>
#include <string.h>
#include <vector>
#include <x86intrin.h>
using namespace std;
void test(int tid) {
std::this_thread::sleep_for (std::chrono::seconds (tid));
unsigned int ui;
uint64_t tid_unique_ = __rdtscp(&ui);
std::cout << "tid: " << tid << ", counter: " << tid_unique_ << ", ui: " << ui << std::endl;
std::this_thread::sleep_for (std::chrono::seconds (1));
}
int main() {
size_t trd_cnt = 3 ;
std::vector<std::thread> threads(trd_cnt);
for (size_t i=0; i< trd_cnt; i++) {
// three threads with tid: 0, 1, 2
// force different threads to run on different cpu cores
threads[i] = std::thread(test, i);
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(i, &cpuset);
int rc = pthread_setaffinity_np(threads[i].native_handle(),
sizeof(cpu_set_t), &cpuset);
if (rc != 0) {
std::cout << "Error calling pthread_setaffinity_np, code: " << rc << "\n";
}
}
for (size_t i=0; i< trd_cnt; i++) {
threads[i].join() ;
}
return 0;
}
所以,这里有两个问题:
- 我的理解正确与否?
- 如何构造一个例子来验证它?
==========更新,根据评论
__rdtscp 将(总是?)在高级 cpu 上的内核之间增加
解决方案
在大多数系统上是的,如果您在线程之间创建同步以确保一个确实在另一个1之后运行。否则,所有赌注都被取消;在另一个线程之前启动一个线程并不能确保其代码首先执行。
脚注 1:例如让一个旋转等待,直到它看到另一个完成的原子存储。或者使用互斥锁并rdtscp
在临界区运行,同时使用一个变量来记录其他线程是否已经存在。
在任何非古代(至少像 Core2 和更新的)上,TSC 以恒定频率(“参考”)频率滴答作响。有关/ CPU 功能的链接和详细信息,以及 TSC 未同步的可能性,请参阅此答案。constant_tsc
nonstop_tsc
我认为实际上大多数现代系统确实在内核之间同步了 TSC,这要归功于主板供应商确保即使在多插槽系统上,RESET 信号也会同时分配给所有内核。固件和操作系统软件注意不要把它搞砸。在单插槽系统上要容易得多,例如具有多核 CPU 的普通台式机,其中所有“额外”内核都在同一个芯片上。
但这并不能保证,并且存在的部分原因rdtscp
(带有处理器 ID 输出)是这种可能性(我认为当 RDTSCP 是新的时,这在旧系统上可能更常见)。
VM 甚至可以使用 CPU 功能来透明地偏移和扩展 TSC(通过硬件支持),在物理机之间迁移 VM,同时保持 TSC 的单调性和频率。不加选择地使用这些功能当然会产生不同步的 TSC,甚至会产生在不同内核上以不同频率运行的 TSC。
TSC 是一个 64 位计数器,通常以 CPU 的额定标签频率计数。在某些 CPU 上,这可能超过 ~4.2 GHz (2^32),因此在快速 CPU 上,高半部分每秒增加一次。 如果计算机已“启动”超过 2^32 秒(数十年),或者如果 TSC 已手动设置为具有较大的偏移量,则 TSC 理论上可以换行。
推荐阅读
- r - 如何获得 100 个样本,每个样本长度为 1000 个样本比例为 AATAA 的 5-mers
- ionic-framework - 当应用程序在离子中关闭时如何调用函数
- c# - 服务接口良好实践
- python - 在情节中更改悬停文本值
- php - 忽略 cakephp 3.6.x 上的 SMTP 异常错误
- rust - How to set the proper lifetime of `self` when calling tokio::spawn?
- sql - Oracle SQL - REGEXP_LIKE 在 CHECK 约束中没有按预期工作
- python-3.x - ModuleNotFoundError: No module named 'sklearn.externals.joblib'
- workday-api - Workday Studio 中使用 AM/PM 的日期时间格式
- python - 如何将 QTableWidget 编辑状态下的单元格大小增加超过行大小?