前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[x86][gcc]PAUSE指令在Skylake上引起的性能问题

[x86][gcc]PAUSE指令在Skylake上引起的性能问题

作者头像
皮振伟
发布2018-12-17 14:37:47
2K0
发布2018-12-17 14:37:47
举报
文章被收录于专栏:皮振伟的专栏

前言: docker部署的相同的业务,Host OS也是相同的版本,但是一段代码跑在E5-2630 v4和Gold 5118上,性能却相差很多。业务在在Gold 5118上,QPS下降到了E5-2630 v4的三分之一左右,而且CPU使用率更高。 Gold 5118是Products formerly Skylake系列,E5-2630 v4是Products formerly Broadwell 系列。按理说,Skylake是更新的架构,性能应该更好才对,然而实际表现却并非如此。 分析: 1,perf 在两台机器分别执行perf,发现在5118上,有些不同的地方,libgomp中出现了热点。 先用md5sum确认两个so是否出现了差异,结果是相同的。 因为libgomp被strip过,所以没有对应的symbol,perf只能拿到热点的IP:0xfc79。 使用#objdump -D得到disassembly code,如下

IP是下一条指令,也就是说 0xfc77的pause指令,是热点的指令。 2,pause 查SDM,pause的说明如下,一般的应用场景是“spin-wait loop”中。

看起来并不能解释上述的问题。 3,pause cycles google了一下,有人提到在skylake上,pause指令的执行的cycles变多了。 写代码实际测试(https://github.com/pacepi/tool/blob/master/pause-cycles.c): #include <stdio.h> static inline unsigned long long rdtsc(void) { unsigned long low, high; asm volatile("rdtsc" : "=a" (low), "=d" (high) ); return ((low) | (high) << 32); } #define CPUPAUSELOOP "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n"\ "pause\n" void bench(int loops, unsigned long rdtsc_cycles) { int i = 0; for (i = 0; i < loops; i++) { unsigned long start, finish, elapsed; start = rdtsc(); asm( CPUPAUSELOOP CPUPAUSELOOP CPUPAUSELOOP CPUPAUSELOOP CPUPAUSELOOP :: :); finish = rdtsc(); elapsed = finish - start - rdtsc_cycles; printf("total = %ld, average = %ld\n", elapsed, elapsed / 100); } } unsigned long benchrdtsc() { unsigned long start, finish, elapsed; start = rdtsc(); finish = rdtsc(); finish = rdtsc(); finish = rdtsc(); finish = rdtsc(); finish = rdtsc(); finish = rdtsc(); finish = rdtsc(); finish = rdtsc(); finish = rdtsc(); finish = rdtsc(); elapsed = finish - start; return elapsed / 10; } int main() { unsigned long rdtsc_cycles = benchrdtsc(); bench(10, rdtsc_cycles); return 0; } 在5118上执行的结果是120,在E5-2630 v4执行的结果是9。pause指令在5118上比2630上执行的时间超过10倍。 这里需要注意的是,测试的时候,需要先确认p-state是powersave模式还是performence模式。 #find /sys/devices/system/cpu -name scaling_governor | xargs cat 4,libgomp libgomp是gcc的一个lib,代码路径https://github.com/gcc-mirror/gcc/tree/master/libgomp

libgomp自己实现了do_spin,继续看cpu_relax的实现

可见,如果拿不到锁,就会执行count次的pause。那么,在5118上,就会执行更长的时间。因为这里是busy loop,执行的时候会被统计成进程的user time。也就是说,同样的这段代码在5118上会使用更长的时间。 而docker基本都会用cgroup限制cpu quota,在5118上,时间片更容易消耗完而被调度走。所以,QPS会有下降。 好在libgomp提供了动态配置count的方法,在启动阶段,如果在环境变量中可以正确找到“GOMP_SPINCOUNT”则使用用户配置,否则就是hard code。

一个很犀利的同事给出了这个问题的暂时解决办法:在5118上pause指令的性能大约下降了14倍,所以“GOMP_SPINCOUNT”的值就是30000000000的14分之1,大约2000000000。在启动前执行#export GOMP_SPINCOUNT=2000000000,问题缓解。 5,glibc 在glibc2.23上,

在glibc2.27上,

这里需要注意的是,尽管#define atomic_spin_nop() asm ("rep; nop"),但是在编译之后,"rep; nop"依然被gcc替换成为“pause”。 在不同版本的glibc使用pthread_spin_lock函数,会出现不同的热点。 后记: 其他的问题,在skylake上如果性能突然变得不好,热点抓到是pause指令,很可能就是这个原因导致。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-11-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AlwaysGeek 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档