使用 pthread 时,使用全局变量的 C++ 显示比指针慢 100%?

Posted

技术标签:

【中文标题】使用 pthread 时,使用全局变量的 C++ 显示比指针慢 100%?【英文标题】:C++ using global variable shows 100% slower than a pointer, when using pthread? 【发布时间】:2017-04-22 10:21:55 【问题描述】:

我有一个相当程序来展示 2 个类似程序的性能,它们都使用 2 个线程进行计算。核心区别在于一个使用全局变量,另一个使用“新”对象,如下所示:

#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[2];
    pthread_create(&tid[0],NULL,addx,&obj.a);
    pthread_create(&tid[1],NULL,addx,&obj.b);
    pthread_join(tid[0],NULL);
    pthread_join(tid[1],NULL);
    return 0;


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

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

很慢,然后我修改了 obj 以动态创建(我希望它会更慢):

#include<pthread.h>
#include<stdlib.h>
struct M
    long a;
    long b;
*obj;//difference 1
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;//difference 2
    pthread_t tid[2];
    pthread_create(&tid[0],NULL,addx,&obj->a);//difference 3
    pthread_create(&tid[1],NULL,addx,&obj->b);//difference 4
    pthread_join(tid[0],NULL);
    pthread_join(tid[1],NULL);
    delete obj;//difference 5
    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 上尝试了 g++,结果相同。 但是这要怎么解释呢?我知道 obj 是全局变量,但 *obj 仍然是全局变量,只是动态创建的。核心区别是什么?

【问题讨论】:

无法重现 test1 (3.54s) 和 test2 (3.55s)。也不应该将-O2 放在.cpp 路径之前吗? 可能与虚假共享有关,但人们会认为虚假共享在这两种实现中都是一个问题。 您的可执行文件是 32 位还是 64 位? 抱歉,什么是虚假分享?我在我的mac和linux上都测试过,结果稳定。 @Troskyvs :当 2 个线程处理位于同一缓存行中的对象时,一个线程的每次访问都会使另一个线程的缓存行无效,从而强制从更高的缓存/主缓存重新加载内存很贵。 (mechanical-sympathy.blogspot.be/2011/07/false-sharing.html) 【参考方案1】:

我认为这确实是因为虚假分享,正如 Unimportant 所建议的那样。

那你可能会问,为什么会有不同呢?

因为count 变量!由于这是一个变量,而size_t 的基础类型恰好是您的long,编译器无法对其进行优化(因为pl 可能指向count)。如果countint,由于严格的别名规则,编译器可以将其优化掉(或者简单地它可以是const size_t)。

所以生成的代码必须每次在循环中读取count

在第一个示例中,countobj 都是全局变量,它们彼此靠近放置。因此,链接器很有可能将这些变量放入同一个高速缓存行。因此写入obj.aobj.b 将使count 的缓存行无效。 所以 CPU 必须同步 count 的读取。

在第二个例子中,obj 分配在堆上,它的地址会离count 足够远,所以它们不会占用相同的缓存行。 count 不需要同步

【讨论】:

以上是关于使用 pthread 时,使用全局变量的 C++ 显示比指针慢 100%?的主要内容,如果未能解决你的问题,请参考以下文章

如何在php中跨线程共享全局变量?

pthread

全局变量的顺序改变了 C++/OpenGL 中的性能

全局写入时出现分段错误

C++多线程怎么实现

C++ 错误:尝试访问全局变量时变量未命名类型