CPP中解决伪共享

原创 2015年11月20日 09:56:53

在学习LMAX的disruptor(无锁环形消息队列)时,接触到了CPU-cacheline伪共享的概念.

总结:
当使用颗粒度很小的atomic来代替锁时,由于cpu高速缓存是成块的对内存进行预读,从而在更改atomic时,导致其他核心缓存进行无必要的刷新,降低了性能.(本核心修改的atomic同时存在于其他核心的缓存中,而修改之后值需要在核心间进行同步,来保证原子性)

这里引用查询到的博文:伪共享(False Sharing)

缓存系统中是以缓存行(cache line)为单位存储的。缓存行是2的整数幂个连续字节,一般为32-256个字节。最常见的缓存行大小是64个字节。当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享。缓存行上的写竞争是运行在SMP系统中并行线程实现可伸缩性最重要的限制因素。有人将伪共享描述成无声的性能杀手,因为从代码中很难看清楚是否会出现伪共享。

为了让可伸缩性与线程数呈线性关系,就必须确保不会有两个线程往同一个变量或缓存行中写。两个线程写同一个变量可以在代码中发现。为了确定互相独立的变量是否共享了同一个缓存行,就需要了解内存布局,或找个工具告诉我们。Intel VTune就是这样一个分析工具。

这里写图片描述

在清楚问题的基本原因后构建代码进行测试:

#include <atomic>
#include <thread>
#include <iostream>
#include <chrono>

using namespace std;

/// The value of 64 bytes is typical for x86/x64 architectures.
const size_t CacheLineSize = 64;

#define LOOP_NUMS 30000000

class nFlag {
public:
    nFlag() {
        flag1_.clear();
        flag2_.clear();
        flag3_.clear();
    }

    atomic_flag flag1_;
    atomic_flag flag2_;
    atomic_flag flag3_;
};

class nPadFlag {
public:
    nPadFlag() {
        flag1_.clear();
        flag2_.clear();
        flag3_.clear();
    }

    uint8_t m_pad1[CacheLineSize - sizeof(atomic_flag)];
    atomic_flag flag1_;
    uint8_t m_pad2[CacheLineSize - sizeof(atomic_flag)];
    atomic_flag flag2_;
    uint8_t m_pad3[CacheLineSize - sizeof(atomic_flag)];
    atomic_flag flag3_;
    uint8_t m_pad4[CacheLineSize - sizeof(atomic_flag)];
};

void f_add(int *num, atomic_flag *flag) {
    for(int i = 0; i < LOOP_NUMS; ++i) {
        while(flag->test_and_set(std::memory_order_acquire));

        *num += 1;

        flag->clear(std::memory_order_release);
    }
}

void main() {
    //int arr[3];
    //memset(arr, 0, sizeof(arr));

    int a = 0, b = 0, c = 0;

    nFlag nor_flag;
    nPadFlag pad_flag;
    {
        auto s_time = chrono::steady_clock::now();
        for(int loop = 0; loop < 20; ++loop) {
            thread th1 = thread(f_add, &a, &(pad_flag.flag1_));
            thread th2 = thread(f_add, &b, &(pad_flag.flag2_));
            thread th3 = thread(f_add, &c, &(pad_flag.flag3_));

            th1.join();
            th2.join();
            th3.join();

            //cout << arr[0] << " " << arr[1] << " " << arr[2] << endl;
        }
        auto t_elapse = chrono::steady_clock::now() - s_time;
        cout << "with pad: " << t_elapse.count() / 1000000 / 20 << endl;
    }
    {
        auto s_time = chrono::steady_clock::now();
        for(int loop = 0; loop < 20; ++loop) {
            thread th1 = thread(f_add, &a, &(nor_flag.flag1_));
            thread th2 = thread(f_add, &b, &(nor_flag.flag2_));
            thread th3 = thread(f_add, &c, &(nor_flag.flag3_));

            th1.join();
            th2.join();
            th3.join();

            //cout << arr[0] << " " << arr[1] << " " << arr[2] << endl;
        }

        auto t_elapse = chrono::steady_clock::now() - s_time;
        cout << "without: " << t_elapse.count() / 1000000 / 20 << endl;
    }
    getchar();
}

在windows10 X64的机器上,使用vs2015编译得到release结果:
with pad: 2024 (ms)
without: 2995 (ms)

规避了false sharing后得在3线程下得到了30%的性能提升

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Ogrekit共享库main.cpp源码

/* ------------------------------------------------------------------------------- This file is part...

Unity64 AStarPath 寻路失效 Bug解决 IOS64 IL2CPP - Bad date/time format in the zip file

把游戏项目迁移到IOS64 上面又出现了自动寻路无效的BUG,在XCode Console中有提示AStarpath异常, Bad date/time format in the zip file 在...

无法上csdn博客的解决方法&cxalloc.cpp(111)&程序可以单步调通,裸奔不行

最近这两天,我们实验室的CSDN博客上不去了,有且只有博客上不去,尝试了各种解决办法。。。修改DNS,使用代理等,不知道是不得法还是怎样,没用,昨天发现问题是域名解析重定向,然后就删了hosts文件的...

对于C++窗口编译一闪而过的解决方法 (DEV CPP下)

对于C++窗口编译一闪而过的解决方法   首先来看一个简单的程序(编译环境为 DEV C++。):  #include   int main()  {     ...

cpp extend python 的一种解决方案

因为python的module机制,python很容易通过c扩充。 官方的方法module,newType:http://docs.python.org/extending/index.html ...

批处理解决实际问题1——将目录下所有文件(*.cpp)分别创建同名文件夹并移入其中

问题描述: 学习OpenGL时,从网上下载了一些源代码,解压后发现所有.cpp文件在同一个目录下,这样直接一个一个全部编译的话,就乱套了,虽然不影响结果但不利于管理。于是,我需要写一个程序将所有*....

c++使用模板时.h和.cpp分离产生的问题分析和解决方案

首先需要认知以下几点 : 编译器只编译cpp文件,不单独编译.h文件 编译器在编译阶段是独立编译的 编译一个cpp文件时,展开包含的头文件,发现一个调用在当前文件无法找到,就标记为一个符号 类模板产生...

关于vs2010(C++ 工程)的异常问题(this is not a valid c/c++ file .CPP)的解决(C++ 初学者)

问题点的描述: 前天在复习上次学过的“创建Windows应用程序项目(在一个窗口中显示一幅图片)”时,我重新建立了一个工程A,以前学习时建的那个工程B已完成编译通过了。在新建的这个工程中我可能在...

Qt中Error:Could not decode "xxx.cpp" with "System"-encoding.Editing not possible.的解决方案

在打开Qt项目中的文件的时候,有时候会遇到Error:Could not decode "xxx.cpp" with"System"-encoding.Editing not possible.这种错...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)