由于cpu cache line机制在共享原子数据操作上带来的硬件干扰对多线程机制的性能影响

news2025/1/18 14:50:01

由于cpu cache line机制在共享原子数据操作上带来的硬件干扰会对对多线程性能造成影响。例如不同的原子数据,位于同一个cpu cache line,这时候一个处理器读取这个cpu cache line这段数据的时候,就会控制这段数据的所有权,其他想要读写这段数据的处理器就会处于等等状态。这种情况就是通常说的数据伪共享。因为等待阻塞,所以影响性能。再延伸到cpu cache硬件的数据存取机制,这种影响会导致严重中的并行性能下降,大幅削弱多核优势。

cppreference中的相关代码如下:

namespace hardware_p01
{

#ifdef __cpp_lib_hardware_interference_size
using std::hardware_constructive_interference_size;
using std::hardware_destructive_interference_size;
#else
// 在 x86-64 │ L1_CACHE_BYTES │ L1_CACHE_SHIFT │ __cacheline_aligned │ ... 上为 64 字节
constexpr std::size_t hardware_constructive_interference_size = 64;
constexpr std::size_t hardware_destructive_interference_size  = 64;
#endif

std::mutex cout_mutex;

constexpr int max_write_iterations{10'000'000}; // 性能评估时间调节

struct alignas(hardware_constructive_interference_size)
    OneCacheLiner
{   // 两个原子变量总共占据一条缓存线(cpu cache line)
    std::atomic_uint64_t x{};
    std::atomic_uint64_t y{};
} oneCacheLiner;

struct TwoCacheLiner
{ // 两个原子变量各自独立占据一条缓存线,总共使用了两条缓存线(cpu cache line)
    alignas(hardware_destructive_interference_size) std::atomic_uint64_t x{};
    alignas(hardware_destructive_interference_size) std::atomic_uint64_t y{};
} twoCacheLiner;

inline auto now() noexcept { return std::chrono::high_resolution_clock::now(); }

template <bool xy>
void oneCacheLinerThread()
{
    const auto start{now()};

    for (uint64_t count{}; count != max_write_iterations; ++count)
        if constexpr (xy)
            oneCacheLiner.x.fetch_add(1, std::memory_order_relaxed);
        else
            oneCacheLiner.y.fetch_add(1, std::memory_order_relaxed);

    const std::chrono::duration<double, std::milli> elapsed{now() - start};
    std::lock_guard                                 lk{cout_mutex};
    std::cout << "oneCacheLinerThread() spent " << elapsed.count() << " ms\n";
    if constexpr (xy)
        oneCacheLiner.x = elapsed.count();
    else
        oneCacheLiner.y = elapsed.count();
}

template <bool xy>
void twoCacheLinerThread()
{
    const auto start{now()};

    for (uint64_t count{}; count != max_write_iterations; ++count)
        if constexpr (xy)
            twoCacheLiner.x.fetch_add(1, std::memory_order_relaxed);
        else
            twoCacheLiner.y.fetch_add(1, std::memory_order_relaxed);

    const std::chrono::duration<double, std::milli> elapsed{now() - start};
    std::lock_guard                                 lk{cout_mutex};
    std::cout << "twoCacheLinerThread() spent " << elapsed.count() << " ms\n";
    if constexpr (xy)
        twoCacheLiner.x = elapsed.count();
    else
        twoCacheLiner.y = elapsed.count();
}

void testMain()
{
    std::atomic_uint64_t ap_01{};
    std::cout << "sizeof(int): " << sizeof(int) << " bytes\n";
    std::cout << "sizeof(long): " << sizeof(long) << " bytes\n";
    std::cout << "sizeof(long long): " << sizeof(long long) << " bytes\n";
    std::cout << "sizeof(std::atomic_uint64_t): " << sizeof(std::atomic_uint64_t) << " bytes\n";
    std::cout << "\n";
    std::cout << "sizeof(oneCacheLiner.x): " << sizeof(oneCacheLiner.x) << " bytes\n";
    std::cout << "sizeof(oneCacheLiner.y): " << sizeof(oneCacheLiner.y) << " bytes\n";
    std::cout << "sizeof(oneCacheLiner): " << sizeof(oneCacheLiner) << " bytes\n";
    std::cout << "\n";
    std::cout << "sizeof(twoCacheLiner.x): " << sizeof(twoCacheLiner.x) << " bytes\n";
    std::cout << "sizeof(twoCacheLiner.y): " << sizeof(twoCacheLiner.y) << " bytes\n";
    std::cout << "sizeof(twoCacheLiner): " << sizeof(twoCacheLiner) << " bytes\n";
    std::cout << "\n";
    // 获得当前系统cpu核心数量
    auto cupCount = std::thread::hardware_concurrency();
    std::cout << "cpu核心数量: " << cupCount << "\n";
    std::cout << "\n";

    std::cout << "__cpp_lib_hardware_interference_size "
#ifdef __cpp_lib_hardware_interference_size
                 " = "
              << __cpp_lib_hardware_interference_size << "\n";
#else
                 "is not defined, use 64 as fallback\n";
#endif

    std::cout
        << "hardware_destructive_interference_size == "
        << hardware_destructive_interference_size << '\n'
        << "hardware_constructive_interference_size == "
        << hardware_constructive_interference_size << "\n\n";

    std::cout
        << std::fixed << std::setprecision(2)
        << "sizeof( OneCacheLiner ) == " << sizeof(OneCacheLiner) << '\n'
        << "sizeof( TwoCacheLiner ) == " << sizeof(TwoCacheLiner) << "\n\n";

    constexpr int max_runs{4};

    int oneCacheLiner_average{0};
    for (auto i{0}; i != max_runs; ++i)
    {
        std::thread th1{oneCacheLinerThread<0>};
        std::thread th2{oneCacheLinerThread<1>};
        th1.join();
        th2.join();
        oneCacheLiner_average += oneCacheLiner.x + oneCacheLiner.y;
    }
    std::cout << "Average time: " << (oneCacheLiner_average / max_runs / 2) << " ms\n\n";

    int twoCacheLiner_average{0};
    for (auto i{0}; i != max_runs; ++i)
    {
        std::thread th1{twoCacheLinerThread<0>};
        std::thread th2{twoCacheLinerThread<1>};
        th1.join();
        th2.join();
        twoCacheLiner_average += twoCacheLiner.x + twoCacheLiner.y;
    }
    std::cout << "Average time: " << (twoCacheLiner_average / max_runs / 2) << " ms\n\n";
}
} // namespace hardware_p01

运行上述代码中的 testMain函数,控制台输出如下:

sizeof(int): 4 bytes
sizeof(long): 4 bytes
sizeof(long long): 8 bytes
sizeof(std::atomic_uint64_t): 8 bytes

sizeof(oneCacheLiner.x): 8 bytes
sizeof(oneCacheLiner.y): 8 bytes
sizeof(oneCacheLiner): 64 bytes

sizeof(twoCacheLiner.x): 8 bytes
sizeof(twoCacheLiner.y): 8 bytes
sizeof(twoCacheLiner): 128 bytes

cpu核心数量: 16

__cpp_lib_hardware_interference_size  = 201703
hardware_destructive_interference_size == 64
hardware_constructive_interference_size == 64

sizeof( OneCacheLiner ) == 64
sizeof( TwoCacheLiner ) == 128

oneCacheLinerThread() spent 206.88 ms
oneCacheLinerThread() spent 208.91 ms
oneCacheLinerThread() spent 256.23 ms
oneCacheLinerThread() spent 264.54 ms
oneCacheLinerThread() spent 167.86 ms
oneCacheLinerThread() spent 173.16 ms
oneCacheLinerThread() spent 198.84 ms
oneCacheLinerThread() spent 207.99 ms
Average time: 209 ms

twoCacheLinerThread() spent 59.24 ms
twoCacheLinerThread() spent 59.37 ms
twoCacheLinerThread() spent 61.98 ms
twoCacheLinerThread() spent 66.52 ms
twoCacheLinerThread() spent 65.99 ms
twoCacheLinerThread() spent 68.66 ms
twoCacheLinerThread() spent 59.04 ms
twoCacheLinerThread() spent 63.32 ms
Average time: 62 ms

由上述代码可以看出,当两个原子变量的数据位于同一个cpu cache line的时候(oneCacheLiner中的x和y)访问耗时,是位于各自独立的cpu cache line的数据(twoCacheLiner中的x和y)访问耗时的3倍多。

顺带提一下阿姆达尔定律(Amdahl's law,Amdahl's argument),这是一个计算机科学界的经验法则,因吉恩·阿姆达尔而得名。它代表了处理器并行运算之后效率提升的能力。

上图公式的含义是: 当程序“串行”部分的耗时用fs来表示的时候,那么性能增益(P)就可以通过处理器数量(N)进行估计。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/967311.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WorkManager的基本使用

目录 一、WorkManager概述1. WorkManager的作用&#xff1a;2. WorkManager的各个角色 二、依赖库的导入三、WorkManager几种基本使用1. 单一任务的执行2. 数据 互相传递3. 多个任务 顺序执行4. 重复执行后台任务5. 约束条件6. 证明 app被杀掉之后&#xff0c;还在后台执行 四、…

中小企业常用的 IT 项目管理软件有哪些?

越热门&#xff0c;越贵的IT项目管理软件越好用吗&#xff1f;对于预算有限的中小型企业来说&#xff0c;如何选择一款适合自己的项目管理工具着实是个头疼的问题。 首先适用于中小型企业使用的 IT 项目管理软件需要具备哪些特点呢&#xff1f; 1、简单易用&#xff1a;中小企…

note_前端框架Vue的安装和简单入门(Windows 11)

1. Vue安装 (1) 下载安装node.js和npm # 下载msi安装包 https://nodejs.org/en# 点击安装包&#xff0c;按提示安装 # 默认安装nodejs, npm, 在线文档; PATH配置# 确认安装是否成功&#xff0c;在dos中输入 node -v # 验证nodejs是否安装成功 npm -v # 验证nodejs包管…

DSP应用技术学习笔记——第一讲

如果一项选择被分解为两个连续的选择&#xff0c;则原来的信息量H应该等于分解后的各个信息量H的加权和 DSP概念 DSP既是数字信号处理&#xff08;Digital Signal Processings&#xff09;的缩写也是数字信号处理器&#xff08;Digital Signal Processor&#xff09;的缩写。…

Lesson4-3:OpenCV图像特征提取与描述---SIFT/SURF算法

学习目标 理解 S I F T / S U R F SIFT/SURF SIFT/SURF算法的原理&#xff0c;能够使用 S I F T / S U R F SIFT/SURF SIFT/SURF进行关键点的检测 SIFT/SURF算法 1.1 SIFT原理 前面两节我们介绍了 H a r r i s Harris Harris和 S h i − T o m a s i Shi-Tomasi Shi−Tomasi…

城市白模三维重建

收费工具&#xff0c;学生党勿扰&#xff0c;闹眼子当勿扰 收费金额&#xff1a;2000元&#xff0c;不能开发票 1 概述 几个月前&#xff0c;一家小公司找我帮忙给他们开发一个程序&#xff0c;可以将一个城市进行白模的三维重建。 报酬大约5K。经过不懈的努力&#xff0c;终于…

亚马逊广告收入突破百亿美元,有望成为下一个收入支柱来源?

据外媒报道&#xff0c;亚马逊新兴的广告业务已经价值数百亿美元&#xff0c;很可能成为其下一个收入支柱来源。 市场研究公司Insider Intelligence的分析师Andrew Lipsman表示&#xff0c;按照目前的发展轨迹&#xff0c;亚马逊广告业务甚至可以与其云计算业务相互抗衡。“毫…

21 自定义miniweb框架|闭包装饰器|log输出

文章目录 前情知识介绍WSIG-miniWeb框架服务器动态资源请求浏览器请求动态页面的全流程WSGIWSGI接口的定义 静态服务器回顾以及改造web 服务器 和 逻辑处理代码 分离 web动态服务器的基本实现带参数的web动态服务器 闭包装饰器闭包闭包的基础使用函数、匿名函数、闭包、对象修改…

Locked勒索病毒:最新变种.locked袭击了您的计算机?

导言&#xff1a; 在今天的数字时代&#xff0c;勒索病毒已经不再是仅仅让数据变得不可访问的小威胁。 .locked 勒索病毒&#xff0c;作为其中的一种&#xff0c;以其高度复杂的加密算法和迅速变化的攻击手法而备受恶意分子喜爱。本文91数据恢复将带您深入了解 .locked 勒索病毒…

【Linux】简单的小程序:进度条

在学习进度条之前&#xff0c;需要学一点预备知识。 1. 预备知识 回车换行 现在的换行符&#xff08;\n&#xff09;其实就是回车式换行符&#xff0c;另起一行&#xff0c;光标指向最新一行的开头。回车符&#xff08;\r&#xff09;是光标指向这一行的开头。 缓冲区 &a…

springboot封装查询快递物流

目录 一、ApiClient代码解读二、ApiService代码解读三、HomeController代码解读四、整体代码五、结果展示 一、ApiClient代码解读 这是一个简单的Spring Boot的RestTemplate客户端&#xff0c;用于执行HTTP请求。 首先&#xff0c;这个类被Component注解标记&#xff0c;这意味…

销量蹭蹭上涨!亚马逊上这几款宿舍神器火爆了!

一、BedShelfie床边置物架 每年返校季&#xff0c;收纳工具都是最畅销的产品。在亚马逊床头柜热销榜单中&#xff0c;这款产品位居第二。过去一个月里&#xff0c;有1000多名用户购买了这件产品。 二、U Brands磁性干擦日历板 目前&#xff0c;亚马逊上这款产品已经卖到断货。…

ubuntu下Anaconda安装与使用教程

前言 好久没用anaconda了&#xff0c;还记得之前用anaconda的欢乐时光。pytorch和paddlepaddle(飞浆)&#xff0c;怀念&#xff0c;可生活&#xff08;换了ubuntu系统之后&#xff09;教会了我残忍&#xff08;可能很难有机会再用windows的anaconda了&#xff09;。找个时间&a…

C语言:动态内存(一篇拿捏动态内存!)

目录 学习目标&#xff1a; 为什么存在动态内存分配 动态内存函数&#xff1a; 1. malloc 和 free 2. calloc 3. realloc 常见的动态内存错误&#xff1a; 1. 对NULL指针的解引用操作 2. 对动态开辟空间的越界访问 3. 对非动态开辟内存使用free释放 4. 使用free释…

代码随想录—力扣算法题:24两两交换链表中的节点.Java版(示例代码与导图详解)

版本说明 当前版本号[20230903]。 版本修改说明20230903初版 24. 两两交换链表中的节点 力扣题目链接 更多内容可点击此处跳转到代码随想录&#xff0c;看原版文件 给定一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后的链表。 你不能只是单纯的改变…

淘宝/天猫获得淘宝商品详情 API 接口文档

item_get-获得淘宝商品详情 API测试工具 注册开通 taobao.item_get 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_sear…

Google colab部署VITS——零门槛快速克隆任意角色声音

目录 序言 查看GPU配置 复制代码库并安装运行环境 选择预训练模型 上传视频链接&#xff08;单个不应长于20分钟&#xff09; 自动处理所有上传的数据 训练质量相关&#xff1a;实验发现目前使用CJ模型勾选ADD_AUXILIARY&#xff0c;对于中/日均能训练出最好的效果&#x…

【数据结构】 二叉树面试题讲解->叁

文章目录 &#x1f30f;引言&#x1f332;[根据二叉树创建字符串](https://leetcode.cn/problems/construct-string-from-binary-tree/submissions/)&#x1f431;‍&#x1f464;题目描述&#xff1a;&#x1f431;‍&#x1f409;示例&#xff1a;&#x1f4cc;示例一&#x…

1921. 消灭怪物的最大数量

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;贪心排序 复杂度分析写在最后 Tag 【贪心】【排序】【数组】 题目来源 1921. 消灭怪物的最大数量 题目解读 dist[i] 是第 i 个怪兽与城市的初始距离&#xff0c;speed[i] 是第 i 个怪兽的移动距离。怪兽的目的是攻击…

Mybatis学习|注解开发、lombok

1.使用注解开发 无需再编写相应的Mapper.xml文件&#xff0c;直接将sql用注解的形式写在Mapper接口的对应方法上即可。 然后因为没有xml文件,所以要在mybatis-config.xml核心配置文件中注册这个Mapper接口&#xff0c;而不用去注册之前的Mapper.xml&#xff0c;这里其实如果用…