如何测量现代x86平台上当前线程/程序的实际时钟周期数?
测量x86平台当前线程的实际时钟周期数
Windows 方案
方法1:结合RDTSC与CPU实际频率计算
现代x86 CPU的RDTSC指令通常以基准频率运行(而非动态变化的实际运行频率),因此要得到实际时钟周期,需先获取CPU当前实际运行频率,再结合RDTSC的差值计算:
- 通过
GetLogicalProcessorInformationEx获取CPU基准频率(RDTSC的计数速率)和当前实际频率; - 用
__rdtscp指令记录起始、结束的TSC值,计算差值后乘以「实际频率/基准频率」得到实际周期数。
示例代码:
#include <windows.h> #include <intrin.h> #include <iostream> // 获取CPU基准频率(单位:Hz) ULONGLONG GetBaseFrequency() { SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* info = nullptr; DWORD bufferSize = 0; GetLogicalProcessorInformationEx(RelationProcessorCore, nullptr, &bufferSize); info = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(bufferSize); GetLogicalProcessorInformationEx(RelationProcessorCore, info, &bufferSize); ULONGLONG baseFreq = info->Processor.MaxMhz * 1000000ULL; free(info); return baseFreq; } // 获取CPU当前实际频率(单位:Hz) ULONGLONG GetCurrentFrequency() { SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* info = nullptr; DWORD bufferSize = 0; GetLogicalProcessorInformationEx(RelationProcessorCore, nullptr, &bufferSize); info = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(bufferSize); GetLogicalProcessorInformationEx(RelationProcessorCore, info, &bufferSize); ULONGLONG currentFreq = info->Processor.CurrentMhz * 1000000ULL; free(info); return currentFreq; } int main() { ULONGLONG baseFreq = GetBaseFrequency(); ULONGLONG currentFreq = GetCurrentFrequency(); unsigned int dummy; ULONGLONG start = __rdtscp(&dummy); // 要测量的代码段 for (int i = 0; i < 1000000; ++i) {} ULONGLONG end = __rdtscp(&dummy); ULONGLONG tscDiff = end - start; // 计算实际时钟周期数 ULONGLONG actualCycles = tscDiff * currentFreq / baseFreq; std::cout << "实际时钟周期数:" << actualCycles << std::endl; return 0; }
方法2:使用硬件性能计数器
Windows提供的性能计数器API可直接读取PERF_COUNTER_CPU_CYCLES,对应实际CPU周期:
#include <windows.h> #include <iostream> int main() { HANDLE hCounter = CreateEvent(nullptr, FALSE, FALSE, nullptr); PERF_COUNTER_INFO counterInfo = {0}; counterInfo.CounterId = PERF_COUNTER_CPU_CYCLES; counterInfo.InstanceId = PERF_INSTANCE_ID_CURRENT; if (PerfQueryCounter(hCounter, &counterInfo, sizeof(counterInfo))) { ULONGLONG start, end; PerfReadCounter(hCounter, &start); // 要测量的代码段 for (int i = 0; i < 1000000; ++i) {} PerfReadCounter(hCounter, &end); std::cout << "实际时钟周期数:" << (end - start) << std::endl; } else { std::cout << "无法获取性能计数器:" << GetLastError() << std::endl; } CloseHandle(hCounter); return 0; }
Linux 方案
方法1:使用perf_event_open读取硬件计数器
通过perf_event_open系统调用直接获取PERF_COUNT_HW_CPU_CYCLES计数器,是最准确的实际CPU周期测量方式:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/perf_event.h> #include <asm/unistd.h> #include <string.h> static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) { return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); } int main() { struct perf_event_attr pe; long long count; int fd; memset(&pe, 0, sizeof(struct perf_event_attr)); pe.type = PERF_TYPE_HARDWARE; pe.size = sizeof(struct perf_event_attr); pe.config = PERF_COUNT_HW_CPU_CYCLES; pe.disabled = 1; pe.exclude_kernel = 1; // 可选:排除内核态周期 pe.exclude_hv = 1; // 可选:排除虚拟机监控器周期 fd = perf_event_open(&pe, 0, -1, -1, 0); if (fd == -1) { fprintf(stderr, "perf_event_open failed\n"); exit(EXIT_FAILURE); } ioctl(fd, PERF_EVENT_IOC_RESET, 0); ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); // 要测量的代码段 for (int i = 0; i < 1000000; ++i) {} ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); read(fd, &count, sizeof(long long)); printf("实际时钟周期数:%lld\n", count); close(fd); return 0; }
编译需链接-lrt,运行可能需要sudo或调整系统性能权限。
方法2:结合RDTSC与CPU频率计算
读取系统文件获取CPU基准频率和当前频率,再用RDTSC差值计算实际周期:
#include <stdio.h> #include <intrin.h> // 读取当前CPU频率(单位:Hz) unsigned long long get_current_cpu_freq() { FILE* f = fopen("/proc/cpuinfo", "r"); char line[256]; unsigned long long freq = 0; while (fgets(line, sizeof(line), f)) { if (sscanf(line, "cpu MHz : %llu", &freq) == 1) { break; } } fclose(f); return freq * 1000000ULL; } // 读取CPU基准频率(单位:Hz) unsigned long long get_base_cpu_freq() { FILE* f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", "r"); unsigned long long freq = 0; fscanf(f, "%llu", &freq); fclose(f); return freq * 1000ULL; // 文件中是kHz,转Hz } int main() { unsigned long long base_freq = get_base_cpu_freq(); unsigned long long current_freq = get_current_cpu_freq(); unsigned int dummy; unsigned long long start = __rdtscp(&dummy); // 要测量的代码段 for (int i = 0; i < 1000000; ++i) {} unsigned long long end = __rdtscp(&dummy); unsigned long long tsc_diff = end - start; unsigned long long actual_cycles = tsc_diff * current_freq / base_freq; printf("实际时钟周期数:%llu\n", actual_cycles); return 0; }
内容的提问来源于stack exchange,提问作者user541686
相关产品推荐
相关产品推荐

