当前位置: 首页 > news >正文

golang 在多线程中避免 CPU 指令重排

发布日期:2024-03-26 16:29:39

pF5Lfc6.jpg

起因

golang 的发明初衷便是多线程,是一门专门用于多线程高并发的编程语言。其独创的 GMP 模型在多线程的开发上提供了很大的便利。

现代计算机基本上都是多核 CPU 的结构。CPU 在进行指令运行的时候,为了提高效率,会在一些情况下对指令进行重排序,其目的是在保持运行结果和不重拍序的指令一致的前提下,提高程序的运行效率。但是对于多线程并行执行来说,我们可能需要对此额外关注,以避免重排对多线程的影响。

英特尔在其 x86/64 体系结构规范第 3 卷 §8.2.3 中列出了几个这样的问题。这里有一个最简单的例子。假设内存中有两个整数 X 和 Y,最初的值都是 0。两个并行运行的处理器执行以下的机器代码:

pF5LR91.png

虽然在这个例子中使用汇编语言,但这确实是说明 CPU 排序的比较好的方式。每个处理器将 1 存储到其中一个整数变量中,然后将另一个整数加载到寄存器中。(r1 和 r2 只是实际 x86 寄存器(如 eax)的占位符名称。)

现在,无论哪个处理器先将 1 写入内存,都很自然地希望另一个处理器读取回该值,这意味着我们最终应该得到 r1=1、r2=1,或者两者都有。但根据英特尔的规范,情况不一定如此。在规范中,在这个例子的结尾,r1 和 r2 都等于 0 是合法的!这可能是一个违反直觉的结果!

理解这一点的一种方法是,与大多数处理器系列一样,英特尔x86/64处理器可以根据某些规则重新排序机器指令的内存交互,只要它永远不会改变单线程程序的执行。特别地,允许每个处理器将存储的效果延迟超过来自不同位置的任何加载。因此,最终可能会出现指令按以下顺序执行的情况:

pF5LW1x.png

程序测试

CPU 指令重排导致的问题

在下面的程序中,来实现上述 CPU 指令重排在多线程中造成的数据不一致现象。下面代码中,声明了 a,b,x,y 四个变量并将其默认值设置为 0。声明两个 go routine 分别执行目标操作(见代码)。正常情况,不管下面 a = 1,x = b,b = 1, y = a 这四条质量如何执行,如果没有重排产生,那么永远不可能出现 x == 0 和 y == 0 同时发生的情况。

但是由于 CPU 指令重排的原因,在实际执行的情况下,在第 1738, 110002, 12987 次测试到了 CPU 指令重排的发生。

func withCpuReordering() {index := 0for {index += 1var a, b int32 = 0, 0var x, y int32 = 0, 0var wg sync.WaitGroupwg.Add(2)go func() {defer wg.Done()a = 1x = b}()go func() {defer wg.Done()b = 1y = a}()wg.Wait()if x == 0 && y == 0 {panic("CPU Reordering occurs!")} else {fmt.Println("Now processing in loop", index)}}
}
绑定 CPU 消除指令重排

上述例子的现象只在多核 CPU 执行的之后才会出现,也就是线程并行执行的时候才会出现。如果我们将上述程序的执行都锁定在一个 CPU 上,也就能避免这种情况的发生。

在下面代码中,我们制定 go routine 最多只能使用一个 CPU。在整个测试过程中,没有出现 x == 0 和 y == 0 同时发生的情况。

func main() {runtime.GOMAXPROCS(1)withCpuReordering()
}

原因在于指令重排的目的在于提高执行效率,而不是改变执行结果。

通过内存屏障消除指令重排

在 Go 语言的 sync/atomic 包中,原子操作函数的实现会使用 CPU 提供的原子操作指令,以实现对共享变量的原子读写操作。这些原子操作指令通常会在硬件层面实现内存屏障(Memory Barrier),以确保对共享变量的读写操作在不同的 CPU 核心之间具有一定的有序性。

在下面的代码中,通过 atomic 包中的原子操作函数代替了上述代码中的赋值操作,从而解决了执行结果不一致的情况。

func withoutCpuReordering() {index := 0for {index += 1var a, b int32 = 0, 0var x, y int32 = 0, 0var wg sync.WaitGroupwg.Add(2)go func() {defer wg.Done()atomic.StoreInt32(&a, 1)atomic.StoreInt32(&x, atomic.LoadInt32(&b))}()go func() {defer wg.Done()atomic.StoreInt32(&b, 1)atomic.StoreInt32(&y, atomic.LoadInt32(&a))}()wg.Wait()if x == 0 && y == 0 {panic("CPU Reordering occurs!")} else {fmt.Println("Now processing in loop", index)}}
}

类似的指令和不同的平台

所有这些不同的 CPU 系列,每个都有独特的指令来强制执行内存排序,编译器根据不同的 CPU 系列将代码编译成不同的指令,并且每个跨平台项目都实现了自己的可移植层。这些都无助于简化无锁编程!这也是最近引入 C++11 原子库标准的部分原因。这是一种标准化的尝试,使编写可移植的无锁代码变得更容易。

比如 mfence 指令特定于 x86/64 的 CPU 架构。如果想使代码更具可移植性,可以将此内在特性封装在预处理器宏中。Linux 内核将其封装在一个名为 smp_mb 的宏,以及相关的宏中,如 smp_rmb 和 smp_wmb,并在不同的体系结构上提供了替代实现。例如,在 PowerPC 上,smp_mb 被实现为 sync。

参考文档:

[1] Memory Reordering Caught in the Act https://preshing.com/20120515/memory-reordering-caught-in-the-act/

相关文章:

golang 在多线程中避免 CPU 指令重排

发布日期:2024-03-26 16:29:39 起因 golang 的发明初衷便是多线程,是一门专门用于多线程高并发的编程语言。其独创的 GMP 模型在多线程的开发上提供了很大的便利。 现代计算机基本上都是多核 CPU 的结构。CPU 在进行指令运行的时候,为了提高…...

自动化更新包文件--shell脚本

自动化更新包文件--shell脚本 背景手动更包自动化更包 背景 作为一名实施工程师,当然也协助做些测试的工作,当产品功能开发后,研发会将本次迭代涉及的前后端包文件提供过来。有时会因为一些原因研发没法现场开发,那就需要我们配合…...

Vue element-plus 导航栏 [el-menu]

导航栏 [el-menu] Menu 菜单 | Element Plus el-menu有很多属性和子标签,为网站提供导航功能的菜单。 常用标签: 它里面有两个子标签。el-menu-item,它其实就是el-menu每一个里面的item,item就是真实匹配到路由的每个栏目&#…...

数据结构——数组

数组定义: 在计算机科学中,数组是由一组元素(值或变量)组成的数据结构,每个元素有至少一个索引或键来标识。 因为数组内的元素是连续存储的,所以数组中元素的地址,可以通过其索引计算出来。 性…...

python asyncio websockets server

python websocket server在收到接受消息处理完后会默认关闭连接。需要在msg_handler里面加个while true就能一直保持连接了。 start_server websockets.serve(msg_handler, "0.0.0.0", 29967) asyncio.get_event_loop().run_until_complete(start_server) asyncio.…...

视频素材免费网站有哪些?8个视频素材库网站下载推荐

在视频创作领域,选择正确的高质量无水印素材网站能够极大地丰富您的作品,让每一帧都鲜活起来。下面,我们继续为您介绍更多优质的视频素材网站,每一个都是您创作旅程中的宝贵资源。 1. 蛙学府(中国) 集合了…...

ChatGPT与传统搜索引擎的区别:智能对话与关键词匹配的差异

引言 随着互联网的快速发展,信息的获取变得比以往任何时候都更加便捷。在数字化时代,人们对于获取准确、及时信息的需求愈发迫切。传统搜索引擎通过关键词匹配的方式为用户提供了大量的信息,然而,这种机械式的检索方式有时候并不…...

xargs后调用bash自定义函数(写该函数文本到脚本, 并引导PATH)

xargs后调用bash自定义函数 需要3步骤,如下 function to_markdown_href_func() { fp$1 #echo $fpecho -e "\n[${fp}](${PREFIX}/${fp})" }BIN/tmp/bin/ F$BIN/to_markdown_href_func.sh mkdir -p $BIN 获得函数to_markdown_href_func的文本 ,写文本到 /tmp/bin/to_ma…...

学术论文写作新利器:ChatGPT技巧详解

ChatGPT无限次数:点击直达 学术论文写作新利器:ChatGPT技巧详解 在如今信息爆炸的时代,学术论文写作变得愈发重要且具有挑战性。随着人工智能技术的不断发展,ChatGPT作为一种强大的写作辅助工具,为学术论文创作者提供了全新的可能…...

Spring整合JDBC

1、引入依赖 <properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><depen…...

详解Qt中的布局管理器

Qt中的布局管理是用于组织用户界面中控件&#xff08;如按钮、文本框、标签等&#xff09;位置和尺寸调整的一种机制。说白了就是创建了一种规则&#xff0c;随着窗口变化其中的控件大小位置跟着变化。Qt提供了多种布局管理器&#xff0c;每种都有其特定用途和特点。以下是对Qt…...

MyBatis 参数重复打印的bug

现象 最近有个需求&#xff0c;需要在mybatis对数据库进行写入操作的时候&#xff0c;根据条件对对象中的某个值进行置空&#xff0c;然后再进行写入&#xff0c;这样数据库中的值就会为空了。 根据网上查看的资料&#xff0c;选择在 StatementHandler 类执行 update 的时候进…...

ES6学习之路:迭代器Iterator和生成器Generator

迭代器 一、知识背景 什么是迭代器 迭代器就是在一个数据集合中不断取出数据的过程迭代和遍历的区别 遍历是把所有数据都取出迭代器注重的是依次取出数据&#xff0c;它不会在意有多少数据&#xff0c;也不会保证能够取出多少或者能够把数据都取完。比如斐波那契额数列&#…...

如何使用 DynamiCrafter Interp Loop 无缝连接两张照片

DynamiCrafter Interp Loop 是一个基于 AI 的工具&#xff0c;可以用来无缝连接两张照片。它使用深度学习技术来生成中间帧&#xff0c;从而使两张照片之间的过渡更加自然流畅。 使用步骤 访问 DynamiCrafter Interp Loop 网站&#xff1a;https://huggingface.co/spaces/Dou…...

今天起,Windows可以一键召唤GPT-4了

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了人工智能中文站https://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT资源 发布在https://it.weoknow.com 更多资源欢迎关注 微软 AI 大计的最后一块拼图完成了&#xff1f; 把 Copilot 按钮放在 Window…...

使用Kaggle API快速下载Kaggle数据集

前言 在使用Kaggle网站下载数据集时&#xff0c;直接在网页上点击下载可能会很慢&#xff0c;甚至会出现下载失败的情况。本文将介绍如何使用Kaggle API快速下载数据集。 具体步骤 安装Kaggle API包 在终端中输入以下命令来安装Kaggle API相关的包&#xff1a; pip install…...

java 通过 microsoft graph 调用outlook(二)

这次提供一些基础调用方式API PS&#xff1a; getMailFolders 接口返回的属性中&#xff0c;包含了未读邮件数量unreadItemCount 一 POM文件 <!-- office 365 --><dependency><groupId>com.google.guava</groupId><artifactId>guava<…...

【机器学习】代价函数

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;机器学习 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…...

[leetcode] 100. 相同的树

给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 示例 1&#xff1a; 输入&#xff1a;p [1,2,3], q [1,2,3] 输出&#xff1a;true示例 2&a…...

08、Lua 函数

Lua 函数 Lua 函数Lua函数主要有两种用途函数定义解析&#xff1a;optional_function_scopefunction_nameargument1, argument2, argument3..., argumentnfunction_bodyresult_params_comma_separated 范例 : 定义一个函数 max()Lua 中函数可以作为参数传递给函数多返回值Lua函…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…...

基于PHP的连锁酒店管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...

tomcat入门

1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效&#xff0c;稳定&#xff0c;易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...