无锁队列是一个非常经典的并行计算数据结构,已经有很多相关的文章以及论文对其进行了探讨。它在DPDK中是一个非常基础且关键的组件,其中包含了很多非常特定的优化技巧。本文试图从顶层设计和具体实现分别来阐述DPDK无锁队列的优点以及正确使用的边界条件。在开始之前,我们还需要先进行两个知识点的铺垫。

RTE_Ring 数据结构

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

struct rte_ring {

›   /* Ring producer status. */

›   struct prod {

›   ›   uint32_t watermark;     /**< Maximum itemsbefore EDQUOT. */

›   ›   uint32_t sp_enqueue;    /**< True, if single producer. */

›   ›   uint32_t size;          /**< Size of ring.*/

›   ›   uint32_t mask;          /**< Mask (size-1)of ring. */

›   ›   volatile uint32_thead;  /**< Producer head.*/

›   ›   volatileuint32_t tail;  /**< Producer tail.*/

›   } prod __rte_cache_aligned;

›   /* Ring consumer status. */

›   struct cons {

›   ›   uint32_t sc_dequeue;    /**< True, if single consumer. */

›   ›   uint32_t size;          /**< Size of thering. */

›   ›   uint32_t mask;          /**< Mask (size-1)of ring. */

›   ›   volatileuint32_t head;  /**< Consumer head.*/

›   ›   volatileuint32_t tail;  /**< Consumer tail.*/

›   } cons __rte_cache_aligned;

›   void*ring[] __rte_cache_aligned;

};

整个数据结构分为3个主要部分:生产者状态信息prod;消费者状态信息 cons;消息队列本身(循环 Ring Buffer)每个单元存储着指向报文内容的指针(64bits)。

每一部分都是Cache Line(64Bytes) 对齐的, 这样就保证CPU可以最有效的 。访问这些数据(不对齐会导致更多的缓存/内存读取操作), 尤其是各个部分之间的数据是互相隔离的,这样不会导致互相干扰。所有的生产者线程只会竞争prod占用的cache line,所有的消费者线程只会竞争cons占用的cache line, ring buffer虽然是共享的,但是实际的访问是通过 prod 和cons两个数据结构来协调控制。在burst size 是32(一次处理32个报文)的情况下,消费者线程很少会和生产者线程竞争同一Cache Line。

CAS 操作

CAS 是 Compare and swap的简称, 这是一个同步原语。它的伪代码如下:

1

2

3

4

5

bool compare_and_swap (int *accum, int *dest, int newval)

{  if (  *accum == *dest ) {

*dest = newval;       returntrue;

}  returnfalse;

}

CAS操作是一个隐式总线加锁的指令,DPDK的X86实现如下:

1

2

3

4

5

6

7

8

9

10

11

staticinlineint                                                          rte_atomic32_cmpset(volatile uint32_t *dst, uint32_t exp,  uint32_t src)

{

›   uint8_t res;

›   asmvolatile(

›   ›   ›    MPLOCKED

›   ›   ›   "cmpxchgl %[src], %[dst];"                                   ›    ›   ›   "sete  %[res];"                                                 ›   ›   ›   : [res] "=a" (res),     /* output */                         ›   ›   ›     [dst] "=m" (*dst)

›   ›   ›   :  [src] "r" (src),      /* input */                          ›   ›   ›     "a" (exp),

›   ›   ›      "m" (*dst)

›   ›   ›   : "memory");            /* no-clobber list */               ›   return res;

}

这段代码的核心是cmpxchg 这条指令,我们将在第二篇文掌中深入讨论这一点。我们现在开始考虑有两个cpu 逻辑core, 同时在竞争队列的使用权。同时向CPU发出cmpxchg指令,总线仲裁器将判断有一个core赢得总线使用权从而获得队列的使用权。任何一个core只需要完成2个动作:赢得使用权后更新状态信息 head;结束使用队列更新状态信息 tail。从时序的角度看一共有三种情景:

我们将开始详细分解这三种场景:在具体实现中 每个core还保留了队列状态信息的head 指针shadow copy,我命名为s[core number]_head 同时每个core都有自己的next指针指向下一个该core可用的队列偏移,我命名为 s[core number]_next。 RTE_RING 数据结构中的状态控制信息我命名为r_head, r_tail, head 指向当前可用的队列偏移, 而tail指向全局未完成的入队操作起始点偏移。 对于场景1,2 来说处理是一样的。

1.起始点 core 0 core 1 的局部信息和全局信息一致。

2.开始竞争队列使用权而core 0胜出,core 0 胜出后,全局的head 更新为 s0_next同时失败的core 1将会再次与全局状态信息head同步,之后再设置s1_next。

3.core 0 完成操作,入队操作完成。

但是,对于第三种情景来说就有些不同。大家可以想象一下,当core0赢得队列使用权之后,core1也赢得了队列使用权, 但是因为某种原因 core0 没有及时的更新tail 那么core1以及完成操作而要更新tail时是怎么样一种情况呢?请看下图。core1 在续core0后也赢得了队列使用权, core0 还没有更新tail。

core1已经完成操作,试图更新tail,但是发现core0还没更新,只好等待。

直到core0完成tail更新。

则皆大欢喜,core1 也可以完成tail的更新。

无锁队列详细分解 — 顶层设计相关推荐

  1. 你应该知道的高性能无锁队列Disruptor

    1.何为队列 听到队列相信大家对其并不陌生,在我们现实生活中队列随处可见,去超市结账,你会看见大家都会一排排的站得好好的,等待结账,为什么要站得一排排的,你想象一下大家都没有素质,一窝蜂的上去结账,不 ...

  2. 无锁队列Disruptor

    1.何为队列 听到队列相信大家对其并不陌生,在我们现实生活中队列随处可见,去超市结账,你会看见大家都会一排排的站得好好的,等待结账,为什么要站得一排排的,你想象一下大家都没有素质,一窝蜂的上去结账,不 ...

  3. 一种高性能无锁队列设计

    分布式与多核处理器在共享资源的情况下均要求在线程安全完成提交的任务,在多线程并发处理大量数据任务情况下为解决多生产者多消费者保证任务队列线程安全设计查找到一种高性能无锁队列设计,进行学习.研究. 主要 ...

  4. Linux无锁共享内存,优秀数据结构学习 - 共享内存无锁队列的实现(二)

    优秀数据结构学习 - 共享内存无锁队列的实现(二) 优秀数据结构学习 - 共享内存无锁队列的实现(二) 1 关键技术 操作系统提供的进程间通信机制有文件.socket.消息队列.管道.共享内存等.其中 ...

  5. 基于数组的无锁队列(译)

    2019独角兽企业重金招聘Python工程师标准>>> 1 引言 最近对于注重性能的应用程序,我们有了一种能显著提高程序性能的选择:多线程.线程的概念实际上已经存在了很长时间.在过去 ...

  6. ZMQ无锁队列的原理与实现

    ZMQ无锁队列的原理与实现 前言 1. 为什么需要⽆锁队列 2. 无锁队列的实现(参考zmq,只支持一写一读的场景) 2.1 无锁队列前言 2.2 原⼦操作函数介绍 2.3 yqueue_t的chun ...

  7. 深入理解高并发技术dpdk无锁队列

    前两周给大家直播分享,并发技术全景(从硬件,操作系统,虚拟机/标准库,编程语言等) 上半场(5个小时):并发/并行技术全景指南 下半场(5个小时):人生的下半场,你准备好了吗 最后我上周还布置了一个作 ...

  8. 无锁编程与有锁编程的效率总结、无锁队列的实现(c语言)

    1.无锁编程与有锁编程的效率 无锁编程,即通过CAS原子操作去控制线程的同步.如果你还不知道什么使CAS原子操作,建议先去查看相关资料,这一方面的资料网络上有很多. CAS实现的是硬件级的互斥,在线程 ...

  9. (Erlang语言)运行时中的无锁队列及其在异步线程中的应用

    本文首先介绍 Erlang 运行时中需要使用无锁队列的场合,然后介绍无锁队列的基本原理及会遇到的问题,接下来介绍 Erlang 运行时中如何通过"线程进度"机制解决无锁队列的问题, ...

  10. Erlang运行时中的无锁队列及其在异步线程中的应用

    本文首先介绍 Erlang 运行时中需要使用无锁队列的场合,然后介绍无锁队列的基本原理及会遇到的问题,接下来介绍 Erlang 运行时中如何通过"线程进度"机制解决无锁队列的问题, ...

最新文章

  1. 百练OJ:2964:日历问题
  2. VTK:图片之StaticImage
  3. python 线性回归 技术方案亮点_基于Python的线性回归实战
  4. difference between JSON Model and client model
  5. 十三、axios框架学习
  6. 第 3 章 UML 类图
  7. 使用QT编写视频播放器总结
  8. C#利用Magick图片压缩
  9. python图书管理实训报告总结_结对项目 —— 图书管理系统实验报告
  10. lettcode算法题目--玛祖游戏
  11. 2014美团校园招聘笔试(10.8北京)
  12. 读《所谓情商高,就是会说话》笔记
  13. C# vb .net实现玻璃桌子效果滤镜
  14. 【洛谷P3651】展翅翱翔之时
  15. 几个常见的 Socket 连接错误及原因[转]
  16. python发明小故事简写_科学发明小故事20字
  17. J.P. Morgan:AI for Investing(脱水解读)
  18. 实现登录和用户信息组件的按需展示
  19. 计算机网络学习记录1
  20. docker容器介绍(五)连载

热门文章

  1. mysql Packet for query is too large (1185 1024)异常
  2. android编译framework架包运行报错 (转)
  3. C#开源爬虫NCrawler源代码解读以及将其移植到python3.2(3)
  4. 两个时间相减(vb.net)
  5. 轻度体验威马Living Pilot智行辅助系统:前期刺激,后期依赖
  6. 自增ID有什么坏处?什么样的场景下不使用自增ID?
  7. Go Get设置代理
  8. 整合spring-boot-starter-data-redis报错解决
  9. java:eclipse:windows开发环境log4j系统找不到指定的路径
  10. Flask 扩展 自定义扩展