`
in355hz
  • 浏览: 228076 次
社区版块
存档分类
最新评论

Disruptor 全解析(6):为什么它这么快 (二) - 神奇的 cacheline 补齐

阅读更多
原文地址:http://mechanitis.blogspot.com/2011/07/dissecting-disruptor-why-its-so-fast_22.html​, 作者是 Trisha Gee, LMAX 公司的一位女工程师。
 

我们多次提到了 Mechanical Sympathy (机器协同?) 这个短语,事实上它甚至是Martin 的博客 标题。它的含义与理解底层计算机硬件的操作原理,然后编程让软件用协同的方式,而不是以违背的方式在硬件上工作有关。

 
关于 RingBuffer​ 与神奇的 cacheline(高速缓存行)补齐,我在 上文​ 提到它以后,收到了不少的评论和问题。因为这合适用画出的漂亮图片来演示——我想,这就是我要解决的下一个问题。
 
Comp Sci 101
 
我喜欢在 LMAX​ 工作的原因之一,就是这里让我在大学和 A 级计算机课程里学到的一切东西在实际上产生意义。常见的,作为一个开发,你可以不理解并且完全抛弃 CPU,数据结构或者 大 O 符号 这些​——我花了整整 10 年职业生涯来忘记这些东西。但是结果证明,如果你的确了解这些知识,并且能够应用这些知识,你就能够写出一些非常巧妙,非常快的代码。
 
所以,这里首先会复习我们在学校学过的知识,并且介绍一些从未学过的知识。请注意,这篇文章包含了大量的过度简化。
 
CPU 是机器的心脏,并且最终由它来执行所有的操作。内存(RAM)是放置你的数据的地方(包括你的程序代码)。我们打算忽略硬盘驱动器(hard driver),网络这些东西,因为 Disruptor​ 的目标是尽可能在内存中运行。
 
CPU 与内存之间有好几层高速缓存(cache),因为即使访问内存也太慢了。如果你正在对一块数据做若干次重复的操作,在执行操作的时候把它加载到一个非常靠近 CPU 的地方是很有意义的(想想一个循环计数器——你一定不想在每次循环中都去内存取出值来递增)。
 
Disruptor 全解析(6):为什么它这么快 (二) - 神奇的 cacheline 补齐
 
越靠近 CPU 的高速缓存(cache),速度越快,尺寸也越小。L1 cache 很小而且速度非常快,紧邻着访问它的 CPU 核心。L2 cache 尺寸要大一点也慢一点,并且仍然只能被一个单独的 CPU 核心访问。L3 cache 在现代的多核计算机上更加常见,它尺寸更大,更慢,并且被单个 CPU 插槽(Slot)上的所有 CPU 核心共享。最后,你拥有一大块内存,由全部 CPU 插槽上的所有 CPU 核心共享。
 
当 CPU 在执行操作的时候,它首先去 L1 cache 查找所需要的数据,然后 L2 cache,然后 L3 cache,最后如果任何 cache 里都没有, 数据就需要全部从内存加载。CPU 访问得越远,操作所需要的时间就越长。因此,如果需要做非常频繁的操作,你最好保证数据在 L1 cache。
 
Martin 和 Make 的 QCon 演示​ 提供了一些高速缓存未命中(cache miss)成本的定性数据:
 
CPU 延时 大约的 CPU 周期
大约的时间
(单位纳秒)
主存 Main memory   ~60-80ns
QPI 总线传输
(between sockets, not drawn)  
  ~20ns
L3 cache  ~40-45 cycles, ~15ns
L2 cache  ~10 cycles, ~3ns
L1 cache  ~3-4 cycles, ~1ns
寄存器 Register   1 cycle  
 
如果你的目标是让端到端(end-to-end)的延迟只有 10ms,用 80ns 去内存里拿一些未命中的数据将成为很重的一块。
 
Cachelines(高速缓存行)
 
现在要注意的有趣事情,是写入高速缓存(cache)的数据不是独立的——比如,不是一个变量,也不是一个指针。cache 是由 cacheline(高速缓存行)组成的,典型的长度是 64 字节,并且高效的对应内存中的一段地址。一个 Java 长整数(long)是 8 字节,因此在一段 cacheline 中最多能放进 8 个长整数(long)的值。
 
Disruptor 全解析(6):为什么它这么快 (二) - 神奇的 cacheline 补齐
(为了简单起见,我忽略了多级缓存)
 
如果你访问的是一个长整数(long)数组,这很不错——数组的一个值被加载到高速缓存(cache)时,会自动的加载另外 7 个。因此你可以非常快速的遍历数组。事实上,你可以非常快速的遍历在连续的内存块上分配的任意数据结构。我做了一个跳转引用从 非常早的一篇 RingBuffer 文章​ 指向这里,它解释了为什么我们在 RingBuffer 中使用数组。
 
相反,如果数据结构的各项不是在内存中相邻的(链表,我盯着你呢),你就得不到自动 cache 加载的优势。当你访问这个数据结构中的每一项时,都有可能遇到 cache miss。
 
不过,所有自动加载都有一个缺陷。想象一下如果长整数(long)不是数组的一部分。假设它只是一个单独的变量。让我们称呼它为“head”,没有特别的理由。然后再假设你的类(Class)里有另外一个变量紧邻着它。让我们任意的称呼它为“tail”。现在,当你加载“head”到高速缓存(cache)时,你也自动的加载了“tail”。
 
Disruptor 全解析(6):为什么它这么快 (二) - 神奇的 cacheline 补齐
 
这听起来不错。除非你意识到“tail”是由生产者(Producer)写入的,而“head”是由消费者(Consumer)写入的。这两个变量在实际使用中是不相关的,并且事实上它们会被可能运行在两个单独的 CPU 核心上的两个独立的线程访问。
 
Disruptor 全解析(6):为什么它这么快 (二) - 神奇的 cacheline 补齐
 
想象一下消费者(Consumer)更新了“head”的值。高速缓存(cache)中的值变化了,内存中的值也变化了,而其他包含“head”的 cacheline 都失效了,因为在这些 cacheline 中没有储存这个“崭新”的值。记住我们只能在整个 cacheline 的层面处理,没办法单独把“head”变量标记成无效。
 
Disruptor 全解析(6):为什么它这么快 (二) - 神奇的 cacheline 补齐
 
现在有一些进程在另外的 CPU 核心上运行,只是想读一下“tail”的值,结果整个 cacheline 都要从内存重新加载。这样,一个与消费者(Consumer)无关的线程想要读一个与“head”无关的值,但是被产生的 cache miss 减慢了。
 
当然,这样更糟糕——如果是两个独立的线程写入同一个 cacheline 上两个不同的变量。每个 CPU 核心都会失效另一个 CPU 核心上的 cacheline,并且不得不在另一个线程每次更新后都重新加载 cacheline。基本上,你已经在这两个线程之间造成了写争用,即使它们访问的是两个不同的变量。
 
这叫做“伪共享​”,因为每次访问“head”你也会拿到“tail”,而每次访问“tail”,你也会同样拿到“head”。所有这一切都发生在幕后,没有编译器警告会告诉你刚刚写的代码在并发访问时会变得非常低效。
 
我们的解决方案——神奇的 cacheline 补齐
 
你会看到 Disruptor 解决了这个问题,至少是在 cacheline 大小为 64 字节或更小的处理器架构下。方法是通过添加补齐来保证 RingBuffer 的序号不会与其他数据共享同一个 cacheline。
 
    public long p1, p2, p3, p4, p5, p6, p7; // cache line padding

    private volatile long cursor = INITIAL_CURSOR_VALUE;

    public long p8, p9, p10, p11, p12, p13, p14; // cache line padding
 
所以这里不会产生伪共享,不会与其他变量意外冲突,也没有不必要的 cache miss。
 
在你的 Entry 对象里这样做也是值得的——如果有不同的消费者(Consumer)写入不同的字段,你也需要保证在每个写入的字段之间没有伪共享。
 
更新:Martin 写了一篇从技术上更加准确和详细的描述 伪共享的文章​,并且发布了性能测试结果。

 

 

分享到:
评论

相关推荐

    Java swing + socket + mysql 五子棋网络对战游戏FiveChess.zip

    五子棋游戏想必大家都非常熟悉,游戏规则十分简单。游戏开始后,玩家在游戏设置中选择人机对战,则系统执黑棋,玩家自己执白棋。双方轮流下一棋,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。 【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python、web、C#、EDA、proteus、RTOS等项目的源码。 【技术】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

    纯C语言实现的控制台有禁手五子棋(带AI)Five-to-five-Renju.zip

    五子棋游戏想必大家都非常熟悉,游戏规则十分简单。游戏开始后,玩家在游戏设置中选择人机对战,则系统执黑棋,玩家自己执白棋。双方轮流下一棋,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。 【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python、web、C#、EDA、proteus、RTOS等项目的源码。 【技术】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

    setuptools-57.1.0.tar.gz

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    setuptools-59.1.1.tar.gz

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    空载损耗计算软件.zip

    空载损耗计算软件

    贪吃蛇.cpp

    贪吃蛇.cpp

    Gobang Game programmed by C(五子棋游戏)Gobang.zip

    五子棋游戏想必大家都非常熟悉,游戏规则十分简单。游戏开始后,玩家在游戏设置中选择人机对战,则系统执黑棋,玩家自己执白棋。双方轮流下一棋,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。 【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python、web、C#、EDA、proteus、RTOS等项目的源码。 【技术】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

    单片机C语言Proteus仿真实例用DS1302与数码管设计的可调电子表

    单片机C语言Proteus仿真实例用DS1302与数码管设计的可调电子表提取方式是百度网盘分享地址

    二叉树的遍历hahahha

    二叉树的遍历hahahha

    setuptools-3.8.1.zip

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    基于linux开发C语言编程五子棋小游戏inset.zip

    五子棋游戏想必大家都非常熟悉,游戏规则十分简单。游戏开始后,玩家在游戏设置中选择人机对战,则系统执黑棋,玩家自己执白棋。双方轮流下一棋,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。 【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python、web、C#、EDA、proteus、RTOS等项目的源码。 【技术】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

    Linux下,C语言实现五子棋程序Linux-Wuziqi.zip

    五子棋游戏想必大家都非常熟悉,游戏规则十分简单。游戏开始后,玩家在游戏设置中选择人机对战,则系统执黑棋,玩家自己执白棋。双方轮流下一棋,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。 【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python、web、C#、EDA、proteus、RTOS等项目的源码。 【技术】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

    飞机大战pygame_demo.zip

    五子棋游戏想必大家都非常熟悉,游戏规则十分简单。游戏开始后,玩家在游戏设置中选择人机对战,则系统执黑棋,玩家自己执白棋。双方轮流下一棋,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。 【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python、web、C#、EDA、proteus、RTOS等项目的源码。 【技术】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

    K210SD卡掉电存储的人脸识别

    K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210SD卡掉电存储的人脸识别K210

    setuptools-47.3.2.zip

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    复制粘贴功能,简单实用

    复制粘贴功能,简单实用,非常好用

    绝缘子缺陷数据集,输电线路巡检

    绝缘子缺陷数据集,总共四类,分别为绝缘子,绝缘子破损,绝缘子闪络,防震锤,总共1688张图片

    Python源码-泰坦尼克之灾.py

    Python源码-泰坦尼克之灾

    setuptools-46.1.2.zip

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    2023-04-06-项目笔记 - 第一百三十一阶段 - 4.4.2.129全局变量的作用域-129 -2024.05.12

    2023-04-06-项目笔记-第一百三十一阶段-课前小分享_小分享1.坚持提交gitee 小分享2.作业中提交代码 小分享3.写代码注意代码风格 4.3.1变量的使用 4.4变量的作用域与生命周期 4.4.1局部变量的作用域 4.4.2全局变量的作用域 4.4.2.1全局变量的作用域_1 4.4.2.129全局变量的作用域_129 - 2024-05-12

Global site tag (gtag.js) - Google Analytics