JCTools Mpsc源码详解(一)

Jctools介绍--jctools是一个Java开源并发非阻塞数据结构实现,其中主要实现了非阻塞Map和非阻塞queue,旨在为Java提供高性能并发数据结构实现;

jctool的特点--为什么性能高:

  1. lazyset--putOrderedObject,使用loadload内存屏障,写不会立即可见
  2. 大量的位运算
  3. 伪共享-通过pad类实现内存填充,使得index的写和element的写不会相互影响
  4. 无锁--cas循环代替锁
  5. 循环数组,避免了频繁GC和对象创建---对应的是内存回收和重分配

Jctools中的mpsc实现类即简单介绍--

  1. MpscArrayQueue
    1. 基于并发环形数据队列ConcurrentCircularArrayQueue的多生产者单消费者队列
    2. 该实现使用快速流方法从队列中进行轮询(进行微小更改以正确发布索引),
    3. 并在producer端扩展了Leslie Lamport并发队列算法(由Martin Thompson提出)。
    4. 只扩容一次,扩容至初始大小的1.5倍
       
  2. MpscBlockingConsumerArrayQueue
    1. 这是消费者端BlockingQueue的部分实现,仅在BaseMpscLinkedArrayQueue中描述的机制之上,但在本例中,保留位用于阻塞而不是调整大小。
  3. MpscChunkedArrayQueue
    1. MPSC阵列队列,从初始容量开始,在初始大小的链接块中增长到maxCapacity。只有当当前区块已满并且元素在调整大小时没有被复制时,队列才会增长,相反,指向新区块的链接会存储在旧区块中,供消费者使用。
    2. 只扩容一次,可以理解为固定大小
  4. MpscCompoundQueue
    1. 内部多个MpscArrayQueue--固定大小
    2. fastPoll 和 slowPoll---fastPoll根据线程ID来决定那个MpscArrayQueue入队,如果该队满则slowPoll---遍历MpscArrayQueue,寻找可以插入的queue;
  5. MpscGrowableArrayQueue
    1. 一个MPSC阵列队列,从 nitialCapacity开始,在链接的块中增长到maxCapacity,每次都将其大小翻倍,直到使用完整的备份阵列。
  6. MpscLinkedQueue
    1. 链表实现无界队列
  7. MpscUnboundedArrayQueue
    1. MPSC阵列队列,从初始容量开始,在初始大小的链接块中无限增长;
    2. 无界队列
  8. MpscUnboundedXaddArrayQueue
    1. 底层实现还是数组链表
    2. 与MpscUnboundArrayQueue不同的是,它的设计目的是在更多producer同时提交时提供更好的扩展

可以看出Jctools提供八种Mpsc的具体实现分别针对不同的生产者消费者场景,Jctools提供的Mpscqueue高性能的主要原因是文章开始提到的几种方案,以及对应的具体实现的一些优化技巧,本身使用还是基本的队列操作,屏蔽了底层的实现细节,具体分析几种经典的实现;

首先大概解释一下Mpscqueue中实现性能优化的几种技术方案--

  1. lazyset:
    1. Mpsc中很多值通过调用native方法putOrderedObject()来实现写入,而putOrderedObject()方法是Unsafe提供的直接内存操作的方法,大概可以理解为putOrderedObject()方法可以通过一个对象中的内存偏移量直接对内存的某个区域写入值,而根据Java内存模型来说分为工作内存和主内存,而这里putOrderedObject()直接写入的是工作内存,期待后续相关操作或依赖os来延时写入主内存,这就导致了putOrderedObject()写入的值一段时间对线程不可见(包括写入线程),但是提高了线程写入的性能;---在生产者消费者模型下,生产者一般不需要读取消息,而Mpsc下我们认为消费者也不会立即读取消息,所以对于消息写入提高了很大的性能
    2. 这里需要了解Java的内存模型,内存屏障以及Java对象内存布局相关的知识,详细可以看我其他文章
  2. 伪共享问题
    1. 现代OS CPU中一般使用多级缓存来解决CPU和内存之间的性能差异,而缓存一般加载缓存行的数据而不是只读取我们程序中需要读的对应的一个值---这里涉及到常用的缓存命中的问题
    2. 而如果不同的线程对相互相邻的变量进行读写操作,就导致了缓存对缓存行的频繁加载和卸载,导致了性能的浪费--
    3. 具体结合上图解释一下什么是伪共享:
      1. 首先,根据线程模型来说,同一个进程下的多个线程是贡献内存资源的--这就是共享
      2. 那什么叫伪共享呢,就是上边提到的线程工作内存,或者具体实现为CPU的多级缓存,每个线程有自己的工作内存,在工作内存中维护了内存中变量的副本,所以站在线程的角度来说变量并不是共享的,而是单独维护副本的;
      3. 伪共享有什么问题呢?我们知道线程维护了变量副本,但是OS提供的数据模型是线程共享模型,所以,线程中对于副本的修改最终会写回到主内存中从而同步给其他线程,达到真正的内存共享,这就设计到内存一致性协议,Java中的是MEMI;但是OS在为线程读取某个变量的值的时候并不是每次只读取或者写入一个值,而是以cacheline为单位来操作的(cacheline 一般为2的n次幂,大小为64B,128B,具体看os或者机器硬件的实现),如上图中,线程1只对变量A做读写,而线程2只对变量B做读写,但是实际内存中A和B是存储在相邻的内存空间中的,这就导致了线程1和线程2同时将AB作为一个cacheline读取到了自己的工作内存中,而由于OS对于内存一致性的维护,导致,线程1对A的修改需要写回到主存并同步给线程2,线程2对B的读写也是同理,这就导致了由于变量存储位置的关系,导致线程1和线程2逻辑上互不影响的操作实际上相互影响到了,从而导致了不必要的性能浪费;--这里需要注意的是,伪共享问题只发生在不同线程对同一cacheline中的不同变量访问的情况下---如果多个线程对同一个变量使用那就必须每次做同步操作,另外如果多个变量存在于不同的cacheline中的话也不存在伪共享问题;
      4. 那如何在避免伪共享问题呢?其实很简单,首先伪共享是OS为了提高性能使用的一种缓存命中策略,我们无法改变OS或者硬件的缓存策略,但是我们可以在代码层面改变变量的存储位置,最好的方法是我们保证大多数情况下会被加载到同一个cacheline的一组变量只被一个线程读写,但是这种情况过于复杂,涉及到并行化计算的策略,另外一种思路就是内存填充,内存填充怎么理解呢?我们在大概了解cacheline的大小的情况下,对某一个变量两边填充不会被使用的无用的数据,保证了一个cacheline中只包含了一个变量,从而使得多个线程使用不同变量的时候不会相互影响到;如下图
      5. Jctools中的Mpsc实现中就通过大量的pad类做为填充类-- 

     总结--Jctools使用无锁和unsafe类中提供的直接内存操作来提高多线程并发下的性能问题;要了解mpsc 的核心优化就要先了解内存共享相关问题;具体的Mpsc实现看后续的文章;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

连接pgsql数据库 sslmode sslrootcert sslkey sslcert 参数的作用

sslmode 参数的作用 sslmode 参数用于指定数据库连接时使用的 SSL 加密模式。SSL(Secure Sockets Layer)是一种加密协议,用于保护数据在客户端和服务器之间的传输过程,以增加数据传输的安全性。sslmode 参数可以设置不同的值&…

Elasticsearch 查询之Function Score Query

前言 ES 的主查询评分模式分为两种,是信息检索领域的重要算法: TF-IDF 算法 和 BM25 算法。 Elasticsearch 从版本 5.0 开始引入了 BM25 算法作为默认的文档评分(relevance scoring)算法。在此之前,Elasticsearch 使…

【Django】Task3 外键的使用、Queryset和Instance

【Django】Task3 外键的使用、Queryset和Instance Task3主要理解数据库外键的使用场景,了解Queryset的功能,通过编写代码体验Queryset中对数据库实例的curd操作,同时了解到Instance的定义。 1.外键的使用 1.1什么是外键 数据表外键是数据…

(二)结构型模式:7、享元模式(Flyweight Pattern)(C++实例)

目录 1、享元模式(Flyweight Pattern)含义 2、享元模式的UML图学习 3、享元模式的应用场景 4、享元模式的优缺点 5、C实现享元模式的简单实例 1、享元模式(Flyweight Pattern)含义 享元模式(Flyweight&#xff09…

redis基本介绍以及在node中使用

文章目录 引言一、什么是redis1. redis简介2. redis的特点3. redis的应用场景 二、redis在windows下安装1. 下载安装2.验证是否安装成功3. 配置环境变量 三、redis-cli常用命令介绍1. redis-cli2. keys *3. set key value4. get key5. exists key6. del key7. info8. flushdb9.…

安装svn插件集成myeclipse2014

第一种: 直接借助myeclipse2014自己的help,当然外网这比较慢了,但是能解决问题的办法就是好办法,能更有效的就是更好的办法,得留着啊。 1.打开myeclipse的help---install from site http://subclipse.tigris.org/upd…

Android设置顶部状态栏颜色

Android设置顶部状态栏颜色_wx637304bacd051的技术博客_51CTO博客

在自定义数据集上使用 Detectron2 和 PyTorch 进行人脸检测

本文讲讲述如何使用Python在自定义人脸检测数据集上微调预训练的目标检测模型。学习如何为Detectron2和PyTorch准备自定义人脸检测数据集,微调预训练模型以在图像中找到人脸边界。 人脸检测是在图像中找到(边界的)人脸的任务。这在以下情况下…

【第16例】IPD开发流程:横向管理工具之袖珍卡

目录 前言 袖珍卡 作者介绍 相关课程 前言 IPD 本身是一个非常庞杂的体系,几乎涵盖了企业的方方面面。 不仅仅是华为,包括一些引入 IPD 的新星科技企业。 他们对 IPD 的引入也是走了先僵化再优化的一个过程。 比如说开始的阶段全盘照抄走流程&…

vue3新建的项目如何配置

如何创建vue项目链接:http://t.csdn.cn/tX8wY 点击src删除一些没有用的东西 首先查看node_modules文件夹有没有pnpm文件夹,有的话删除node_modules文件夹 重新使用npm i进行安装 在APP.vue下面删除无用代码 删除前 删除后 在views下面找到Home首页删…

androidstudio Please specify a signing configuration for this variant (release)

当直接运行release版本时,报错Error: The apk for your currently selected variant cannot be signed. Please specify a signing configuration for this variant (package64-release). 解决报错:添加签名,signingConfigs 写在buildTypes前…

ffmpeg合并mp4视频文件

下载ffmpeg Download FFmpeg 2配置环境 右键此电脑-》属性-》高级系统设置 环境变量-》path 解压上面ffmpeg压缩包,找到bin目录,复制完整路径,添加到path环境变量中 测试ffmpeg ffmpeg合并MP4文件 创建一个文本文件,例如inpu…

C# 使用递归方法实现汉诺塔步数计算

C# 使用递归方法实现汉诺塔步数计算 Part 1 什么是递归Part 2 汉诺塔Part 3 程序 Part 1 什么是递归 举一个例子:计算从 1 到 x 的总和 public int SumFrom1ToX(int x) {if(x 1){return 1;}else{int result x SumFrom1ToX_2(x - 1); // 调用自己return result…

耕地单目标语义分割实践——Pytorch网络过程实现理解

一、卷积操作 (一)普通卷积(Convolution) (二)空洞卷积(Atrous Convolution) 根据空洞卷积的定义,显然可以意识到空洞卷积可以提取到同一输入的不同尺度下的特征图&…

巨人互动|Google开户Google Trends搜索数据分析工具介绍

Google Trends是一款免费的在线工具,可以帮助用户了解不同关键字和主题的搜索趋势,并分析它们在不同地理位置和时间段中的搜索活动。这个工具可以为各种用户提供极大的价值,包括商家、营销人员、媒体从业者和学术研究人员。在本文中&#xff…

vue中css修改滚动条样式

vue中css修改滚动条样式 效果图: 代码(在app.vue中全局增加下面样式即可): &::-webkit-scrollbar {width: 8px;height: 8px;border-radius: 3px;}/*定义滚动条轨道 内阴影圆角*/&::-webkit-scrollbar-track {//-webkit-box-shadow: inset 0 0 …

fatal: not a git repository (or any of the parent directories): .git

提示说没有.git这样一个目录 在命令行 输入 git init 然后回车就好了 git remote add origin https:/.git git push -u origin "master"

uniapp 企业微信侧边栏开发网页授权 注入企业权限 注入应用权限 获取userid(2)

1、网页授权,获取code 代码: oauthUrl() {const that thisuni.removeStorageSync(code)let REDIRECT_URI encodeURIComponent(window.location.href)let CORPID webConfig.appIdlet url https://open.weixin.qq.com/connect/oauth2/authorize?appi…

Tomcat运行后localhost:8080访问自己编写的网页

主要是注意项目结构,home.html放在src/resources/templates下的home.html下,application.properties可以不做任何配置。还有就是关于web包的位置,作者一开始将web包与tabtab包平行,访问8080出现了此类报错: Whitelabel…

不负众望~历时4年修炼,这本册子终于成书了(文末赠书)

名字:阿玥的小东东 学习:Python、C/C 主页链接:阿玥的小东东的博客_CSDN博客-python&&c高级知识,过年必备,C/C知识讲解领域博主 目录 精进Spring Boot首选读物 “小册”变“大书”,彻底弄懂Spring Boot 全方位配套资源…