RDTSC开销中的差异

如何解决RDTSC开销中的差异

| 我在尝试在某些原始图像处理操作中使用SIMD指令内部函数进行实验时,正在构建一个微基准来测量性能变化。但是,编写有用的微基准测试很困难,因此我想首先了解(并尽可能消除)尽可能多的变异和错误来源。 我必须考虑的一个因素是测量代码本身的开销。我正在使用RDTSC进行测量,并且正在使用以下代码查找测量开销:
extern inline unsigned long long __attribute__((always_inline)) rdtsc64() {
    unsigned int hi,lo;
        __asm__ __volatile__(
            \"xorl %%eax,%%eax\\n\\t\"
            \"cpuid\\n\\t\"
            \"rdtsc\"
        : \"=a\"(lo),\"=d\"(hi)
        : /* no inputs */
        : \"rbx\",\"rcx\");
    return ((unsigned long long)hi << 32ull) | (unsigned long long)lo;
}

unsigned int find_rdtsc_overhead() {
    const int trials = 1000000;

    std::vector<unsigned long long> times;
    times.resize(trials,0.0);

    for (int i = 0; i < trials; ++i) {
        unsigned long long t_begin = rdtsc64();
        unsigned long long t_end = rdtsc64();
        times[i] = (t_end - t_begin);
    }

    // print frequencies of cycle counts
}
运行此代码时,我得到如下输出:
Frequency of occurrence (for 1000000 trials):
234 cycles (counted 28 times)
243 cycles (counted 875703 times)
252 cycles (counted 124194 times)
261 cycles (counted 37 times)
270 cycles (counted 2 times)
693 cycles (counted 1 times)
1611 cycles (counted 1 times)
1665 cycles (counted 1 times)
... (a bunch of larger times each only seen once)
我的问题是: 上面的代码生成的周期计数的双峰分布的可能原因是什么? 为什么最快的时间(234个周期)只发生少数几次—在极不寻常的情况下可以减少计数吗? 更多信息 平台: Linux 2.6.32(Ubuntu 10.04) g ++ 4.4.3 Core 2 Duo(E6600);这具有恒定速率的TSC。 SpeedStep已关闭(处理器设置为性能模式并以2.4GHz运行);如果以“按需”模式运行,则在243和252个周期中会出现两个峰值,而在360和369个周期中会出现两个(大概对应的)峰值。 我正在使用
sched_setaffinity
将进程锁定到一个内核。如果我依次在每个内核上运行测试(即,锁定到内核0并运行,然后锁定到内核1并运行),则两个内核会得到相似的结果,除了234个周期的最快时间往往会略有发生核心1的次数少于核心0的次数。 编译命令为:
g++ -Wall -mssse3 -mtune=core2 -O3 -o test.bin test.cpp
GCC为核心循环生成的代码是:
.L105:
#APP
# 27 \"test.cpp\" 1
    xorl %eax,%eax
    cpuid
    rdtsc
# 0 \"\" 2
#NO_APP
    movl    %edx,%ebp
    movl    %eax,%edi
#APP
# 27 \"test.cpp\" 1
    xorl %eax,%eax
    cpuid
    rdtsc
# 0 \"\" 2
#NO_APP
    salq    $32,%rdx
    salq    $32,%rbp
    mov %eax,%eax
    mov %edi,%edi
    orq %rax,%rdx
    orq %rdi,%rbp
    subq    %rbp,%rdx
    movq    %rdx,(%r8,%rsi)
    addq    $8,%rsi
    cmpq    $8000000,%rsi
    jne .L105
    

解决方法

        
RDTSC
可能由于多种原因而返回不一致的结果: 在某些CPU(尤其是某些较旧的Opterons)上,TSC在内核之间不同步。听起来您已经通过使用
sched_setaffinity
处理了-太好了! 如果在代码运行时触发OS计时器中断,则会在运行时引入延迟。没有实际的方法可以避免这种情况;只是抛出了异常高的价值。 在CPU中进行流水线化处理有时可能会使您在紧密循环中向任一方向倾斜几个周期。完全有可能使某些循环以非整数个时钟周期运行。 快取!根据CPU缓存的变化情况,内存操作(如写入“ 7”)的速度可能会有所不同。在这种情况下,您很幸运正在使用的
std::vector
实现只是一个平面数组。即便如此,这种写法也能使事情顺利进行。这可能是此代码最重要的因素。 我对Core2微体系结构的专家还不够,不能确切地说出为什么要获得这种双峰分布,或者代码运行速度快了28倍,但这可能与上述原因之一有关。     ,        如果您要确保确实执行了
rdtsc
之前的指令,则英特尔程序员手册建议您使用
lfence;rdtsc
rdtscp
。这是因为
rdtsc
本身并不是序列化指令。     ,您应确保在操作系统级别禁用了频率调节/绿色功能。重新启动机器。否则,您可能会遇到内核具有不同步的时间戳计数器值的情况。 到目前为止,243读数是最常见的,这是使用它的原因之一。另一方面,假设您的经过时间小于249:减去开销并得到下溢。由于算术是无符号的,因此您将得到巨大的结果。这个事实代表使用最低读数(243)。精确测量只有几个周期长的序列非常困难。在典型的x86 @几个GHz上,我建议不要使用短于10ns的时序,即使在这样的长度下,它们通常也离坚不可摧。 我在这里剩下的答案就是我要做的事情,如何处理结果以及对主题的推理。 至于开销,最简单的方法是使用这样的代码
unsigned __int64 rdtsc_inline (void);
unsigned __int64 rdtsc_function (void);
第一种形式将rdtsc指令发送到生成的代码中(与您的代码一样)。第二个将导致调用函数,执行rdtsc和返回指令。也许它将生成堆栈帧。显然,第二种形式比第一种形式慢得多。 然后可以编写用于开销计算的(C)代码
unsigned __int64 start_cycle,end_cycle;    /* place these @ the module level*/

unsigned __int64 overhead;

/* place this code inside a function */

start_cycle=rdtsc_inline();
  end_cycle=rdtsc_inline();
overhead=end_cycle-start_cycle;
如果您使用的是内联变体,您将获得较低的开销。您还冒着计算开销的风险,该开销大于“应该”(特别是对于函数形式),这反过来意味着,如果您测量非常短/快速的序列,则可能会遇到先前计算的开销大于测量本身。当您尝试调整开销时,会出现下溢情况,这将导致混乱的情况。最好的解决方法是 将开销数倍,并始终使用获得的最小值, 不要测量很短的代码序列,因为您可能会遇到流水线化的效果,这需要在rdtsc指令和 如果您必须测量非常短的序列,则将结果视为指示,而不是事实 之前,我已经讨论了如何处理该线程中的结果。 我要做的另一件事是将测量代码集成到应用程序中。开销微不足道。计算完结果后,我将其发送到一个特殊的结构,在其中计算测量次数,将x和x ^ 2值相加并确定最小和最大测量值。稍后,我可以使用数据来计算平均值和标准偏差。结构本身已建立索引,我可以测量不同的性能方面,例如各个应用程序功能(“功能性能”),在cpu上花费的时间,磁盘读/写,网络读/写(“非功能性能”)等。 如果以这种方式对应用程序进行检测并从一开始就对其进行监视,我预计它将大大降低其在生命周期内出现性能问题的风险。     

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-