cdsfiui 发表于 2017-04-22 18:19

多线程,使用全局变量比使用全局指针慢了一倍,为什么?

我有下面一个这样的测试程序:

        #include<pthread.h>
        #include<stdlib.h>
        struct M{
          long a;
          long b;
        }obj;
        size_t count=2000000000;
        void* addx(void*args){
          long*pl=(long*)args;
          for(size_t i=0;i<count;++i)
                (*pl)*=i;
          return NULL;
        }
        int main(int argc,char*argv[]){
          pthread_t tid;
          pthread_create(&tid,NULL,addx,&obj.a);
          pthread_create(&tid,NULL,addx,&obj.b);
          pthread_join(tid,NULL);
          pthread_join(tid,NULL);
          return 0;
        }

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

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

        real        0m3.626s
        user        0m6.595s
        sys        0m0.009s

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

        #include<pthread.h>
        #include<stdlib.h>
        struct M{
          long a;
          long b;
        }*obj;
        size_t count=2000000000;
        void* addx(void*args){
          long*pl=(long*)args;
          for(size_t i=0;i<count;++i)
                (*pl)*=i;
          return NULL;
        }
        int main(int argc,char*argv[]){
          obj=new M;
          pthread_t tid;
          pthread_create(&tid,NULL,addx,&obj->a);
          pthread_create(&tid,NULL,addx,&obj->b);
          pthread_join(tid,NULL);
          pthread_join(tid,NULL);
          delete obj;
          return 0;
        }

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

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

        real        0m1.880s
        user        0m3.745s
        sys        0m0.007s

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


xinglp 发表于 2017-04-23 09:06

本帖最后由 xinglp 于 2017-04-23 09:09 编辑

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


wlmqgzm 发表于 2017-04-23 15:11

我猜测可能是 false_sharing 造成的, 后一种方法虽然慢, 但是更多的代码, 就减少了冲突的发生.

lxyscls 发表于 2017-04-24 09:39

本帖最后由 lxyscls 于 2017-05-09 09:24 编辑

回复 1# cdsfiui

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


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

cdsfiui 发表于 2017-04-24 09:52

回复 3# wlmqgzm

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

blake326 发表于 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]
查看完整版本: 多线程,使用全局变量比使用全局指针慢了一倍,为什么?