免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4185 | 回复: 5
打印 上一主题 下一主题

[C++] 多线程,使用全局变量比使用全局指针慢了一倍,为什么? [复制链接]

论坛徽章:
2
2015年迎新春徽章
日期:2015-03-04 10:16:532015元宵节徽章
日期:2015-03-06 15:53:22
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2017-04-22 18:19 |只看该作者 |倒序浏览
我有下面一个这样的测试程序:

  1.         #include<pthread.h>
  2.         #include<stdlib.h>
  3.         struct M{
  4.             long a;
  5.             long b;
  6.         }obj;
  7.         size_t count=2000000000;
  8.         void* addx(void*args){
  9.             long*pl=(long*)args;
  10.             for(size_t i=0;i<count;++i)
  11.                 (*pl)*=i;
  12.             return NULL;
  13.         }
  14.         int main(int argc,char*argv[]){
  15.             pthread_t tid[2];
  16.             pthread_create(&tid[0],NULL,addx,&obj.a);
  17.             pthread_create(&tid[1],NULL,addx,&obj.b);
  18.             pthread_join(tid[0],NULL);
  19.             pthread_join(tid[1],NULL);
  20.             return 0;
  21.         }
复制代码

用clang来测试,注意已经加了-O2优化选项:

  1.         clang++ test03_threads.cpp -o test03_threads -lpthread -O2 && time ./test03_threads

  2.         real        0m3.626s
  3.         user        0m6.595s
  4.         sys        0m0.009s
复制代码

看起来挺慢的,我把全局obj改成*obj,运行时创建。我期待这个会更慢一点:

  1.         #include<pthread.h>
  2.         #include<stdlib.h>
  3.         struct M{
  4.             long a;
  5.             long b;
  6.         }*obj;
  7.         size_t count=2000000000;
  8.         void* addx(void*args){
  9.             long*pl=(long*)args;
  10.             for(size_t i=0;i<count;++i)
  11.                 (*pl)*=i;
  12.             return NULL;
  13.         }
  14.         int main(int argc,char*argv[]){
  15.             obj=new M;
  16.             pthread_t tid[2];
  17.             pthread_create(&tid[0],NULL,addx,&obj->a);
  18.             pthread_create(&tid[1],NULL,addx,&obj->b);
  19.             pthread_join(tid[0],NULL);
  20.             pthread_join(tid[1],NULL);
  21.             delete obj;
  22.             return 0;
  23.         }
复制代码

但是出乎我的意料,变快了:

  1.         clang++ test03_threads_new.cpp -o test03_threads_new -lpthread -O2 && time ./test03_threads_new

  2.         real        0m1.880s
  3.         user        0m3.745s
  4.         sys        0m0.007s
复制代码

而且快了差不多100%。我用linux+gcc测试了一下,一样的结果。
这到底是什么,什么原因造成了这么大的性能差异?


论坛徽章:
5
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:53:172015亚冠之水原三星
日期:2015-06-02 16:34:202015年亚冠纪念徽章
日期:2015-10-19 18:13:37程序设计版块每日发帖之星
日期:2015-11-08 06:20:00
2 [报告]
发表于 2017-04-23 09:06 |只看该作者
本帖最后由 xinglp 于 2017-04-23 09:09 编辑

gcc 6.3.0
glibc 2.25
双核环境
-O2 -O3 以及不加 没有任何差异


论坛徽章:
9
程序设计版块每日发帖之星
日期:2015-10-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-01 06:20:00程序设计版块每日发帖之星
日期:2015-11-02 06:20:00每日论坛发贴之星
日期:2015-11-02 06:20:00程序设计版块每日发帖之星
日期:2015-11-03 06:20:00程序设计版块每日发帖之星
日期:2015-11-04 06:20:00程序设计版块每日发帖之星
日期:2015-11-06 06:20:00数据库技术版块每周发帖之星
日期:2015-12-02 15:02:47数据库技术版块每日发帖之星
日期:2015-12-08 06:20:00
3 [报告]
发表于 2017-04-23 15:11 |只看该作者
我猜测可能是 false_sharing 造成的, 后一种方法虽然慢, 但是更多的代码, 就减少了冲突的发生.

论坛徽章:
14
水瓶座
日期:2014-06-10 09:51:0215-16赛季CBA联赛之江苏
日期:2017-11-27 11:42:3515-16赛季CBA联赛之八一
日期:2017-04-12 14:26:2815-16赛季CBA联赛之吉林
日期:2016-08-20 10:43:1215-16赛季CBA联赛之广夏
日期:2016-06-23 09:53:58程序设计版块每日发帖之星
日期:2016-02-11 06:20:00程序设计版块每日发帖之星
日期:2016-02-09 06:20:0015-16赛季CBA联赛之上海
日期:2015-12-25 16:40:3515-16赛季CBA联赛之广夏
日期:2015-12-22 09:39:36程序设计版块每日发帖之星
日期:2015-08-24 06:20:002015亚冠之德黑兰石油
日期:2015-08-07 09:57:302015年辞旧岁徽章
日期:2015-03-03 16:54:15
4 [报告]
发表于 2017-04-24 09:39 |只看该作者
本帖最后由 lxyscls 于 2017-05-09 09:24 编辑

回复 1# cdsfiui

long a 和 long b之间不存在"伪共享",因为会被优化到寄存器里面去


struct m和int count间存在"伪共享"

论坛徽章:
2
2015年迎新春徽章
日期:2015-03-04 10:16:532015元宵节徽章
日期:2015-03-06 15:53:22
5 [报告]
发表于 2017-04-24 09:52 |只看该作者
回复 3# wlmqgzm

更多的代码就能减少冲突的发生吗?

论坛徽章:
0
6 [报告]
发表于 2017-04-30 10:05 |只看该作者
            for(size_t i=0;i<count;++i)
第一个方法方法, count 和struct M再同一个cache line。
每次判断count的时候都要读内存,由于两个core在不断修改该内存。所以这里较大概率发生cache miss。 (invalid -> shared状态)

http://blog.csdn.net/muxiqingyang/article/details/6615199

I(Invalid)
       

Local Read
       

如果其它Cache没有这份数据,本Cache从内存中取数据,Cache line状态变成E;

如果其它Cache有这份数据,且状态为M,则将数据更新到内存,本Cache再从内存中取数据,2个Cache 的Cache line状态都变成S;

如果其它Cache有这份数据,且状态为S或者E,本Cache从内存中取数据,这些Cache 的Cache line状态都变成



解决方法:
1. 结构体增加__cacheline_aligned对齐属性。
2. count写死成常数。

评分

参与人数 1信誉积分 +10 收起 理由
lxyscls + 10 还是您讲的靠谱,count的位置

查看全部评分

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP