提升无锁 spsc_queue 缓存内存访问

2023-11-25

我需要非常关注当前多线程项目中的速度/延迟。

我试图更好地理解缓存访问。我不清楚无锁队列(例如 boost::lockfree::spsc_queue)如何在缓存级别访问/使用内存。

我见过使用队列,其中需要由消费者核心操作的大对象的指针被推入队列。

如果消费者核心从队列中弹出一个元素,我认为这意味着该元素(在本例中为指针)已经加载到消费者核心的 L2 和 L1 缓存中。但是要访问该元素,是否不需要通过从 L3 缓存或跨互连(如果另一个线程位于不同的 cpu 套接字上)查找和加载元素来访问指针本身?如果是这样,那么简单地发送可由消费者处理的对象的副本可能会更好吗?

谢谢。


C++ 主要是按需付费生态系统。

任何常规队列都会让you选择存储语义(按值或按引用)。

然而,这次您订购了一些特别的东西:您订购了一个无锁队列。 为了实现无锁,它必须能够将所有可观察的修改操作作为原子操作执行。这自然限制了这些操作中可以直接使用的类型。

您可能会怀疑是否有可能拥有超出系统本机寄存器大小的值类型(例如,int64_t).

好问题。

输入环形缓冲区

事实上,任何基于节点的容器只需要所有修改操作的指针交换,这在所有现代架构上都是原子的。 但做任何涉及copying多个不同的内存区域,以非原子顺序,真的构成了一个无法解决的问题吗?

No. Imagine a flat array of POD data items. Now, if you treat the array as a circular buffer, one would just have to maintain the index of the buffer front and end positions atomically. The container could, at leisure update in internal 'dirty front index' while it copies ahead of the external front. (The copy can use relaxed memory ordering). Only as soon as the whole copy is known to have completed, the external front index is updated. This update needs to be in acq_rel/cst memory order[1].

只要容器能够保护以下不变量front永远不会完全环绕并到达back,这是一笔甜蜜的交易。我认为这个想法在 Disruptor Library(以 LMAX 闻名)中得到了普及。你得到机械共振 from

  • 读/写时的线性内存访问模式
  • 如果您可以使记录大小与(多个)物理缓存行对齐,那就更好了
  • 所有数据都是本地的,除非 POD 包含该记录之外的原始引用

如何提升spsc_queue实际上这样做吗?

  1. 是的,spqc_queue 将原始元素值存储在连续对齐的内存块中:(例如,来自compile_time_sized_ringbuffer这是其基础spsc_queue具有静态供电的最大容量:)

    typedef typename boost::aligned_storage<max_size * sizeof(T),
                                            boost::alignment_of<T>::value
                                           >::type storage_type;
    
    storage_type storage_;
    
    T * data()
    {
        return static_cast<T*>(storage_.address());
    }
    

    (元素类型T甚至不需要是 POD,但它需要既可默认构造又可复制)。

  2. 是的,读写指针是原子整数值。请注意,Boost 开发人员已注意应用足够的填充以避免虚假分享在读/写索引的缓存行上:(来自ringbuffer_base):

    static const int padding_size = BOOST_LOCKFREE_CACHELINE_BYTES - sizeof(size_t);
    atomic<size_t> write_index_;
    char padding1[padding_size]; /* force read_index and write_index to different cache lines */
    atomic<size_t> read_index_;
    
  3. 事实上,正如您所看到的,读端或写端都只有“内部”索引。这是可能的,因为只有一个写入线程,也只有一个读取线程,这意味着只能有more写操作结束时的空间超出预期。

  4. 还有其他一些优化:

    • 支持它的平台的分支预测提示(unlikely())
    • 可以一次推送/弹出一系列元素。如果您需要从一个缓冲区/环形缓冲区虹吸到另一个缓冲区/环形缓冲区,这应该会提高吞吐量,特别是如果原始元素大小不等于缓存行(的整数倍)
    • 尽可能使用 std::uninitialized_copy
    • 普通构造函数/析构函数的调用将在实例化时进行优化
    • 在所有主要标准库实现上,unitialized_copy 将被优化为 memcpy(这意味着,如果您的架构支持,将使用 SSE 指令)

总而言之,我们看到了环形缓冲区的最佳想法

使用什么

Boost 为您提供了所有选择。你can选择使您的元素类型成为指向消息类型的指针。但是,正如您在问题中已经提出的那样,这种间接级别会减少引用的局部性,并且可能不是最佳的。

另一方面,如果复制成本高昂,则将完整消息类型存储在元素类型中可能会变得昂贵。至少要尝试使元素类型很好地适合缓存行(在 Intel 上通常为 64 字节)。

因此,在实践中,您可能会考虑将经常使用的数据存储在值中,并使用指针引用较少使用的数据(除非遍历指针,否则指针的成本会很低)。

如果您需要该“附件”模型,请考虑对引用的数据使用自定义分配器,以便您也可以在那里实现内存访问模式。

让您的分析器指导您。


[1] I suppose say for spsc acq_rel should work, but I'm a bit rusty on the details. As a rule, I make it a point not to write lock-free code myself. I recommend anyone else to follow my example :)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

提升无锁 spsc_queue 缓存内存访问 的相关文章

  • 将 boost::iostreams::mapped_file_source 与宽字符串一起使用

    如果我使用窄字符串实例化mapped file source boost 1 46 1 如下所示 我不会遇到问题 boost iostreams mapped file source m file testfile txt 但是 如果我尝试
  • WPF 每次打开和关闭窗口时都会增加内存

    我已经做了非常简单的测试 只是为了了解 wpf 如何与内存一起工作 我用一个窗口创建一个项目 其中有一个Button 第二个窗口完全是空的 当我按下Button单击打开第二个窗口 窗口 1 后面的代码
  • Java 内存错误:无法创建新的本机线程

    运行 java 服务器时 我在 UNIX 服务器上收到此错误 Exception in thread Thread 0 java lang OutOfMemoryError unable to create new native threa
  • VMA和ELF段之间的关系

    我需要确定 ELF 可执行文件的可加载段的 VMA VMA 可以从以下位置打印 proc pid maps VMA 之间的关系如下所示maps可加载段对我来说也很清楚 每个段由一个或多个 VMA 组成 内核使用什么方法从 ELF 段形成 V
  • Java BigDecimal 内存使用情况?

    是否有一个指南来估计一个程序消耗的内存量BigDecimal 寻找与这些类似的东西指导方针 http www javamex com tutorials memory string memory usage shtml用于估计String内
  • C# 中的字符串常量内存池

    大家都知道 Net框架中String对象是直接存储在堆内存中的 我只是想了解 Net 框架中是否为字符串保留了内存 在java中 有一个为字符串保留的内存 称为SCMP 字符串常量内存池 字符串在其中像堆内存中的其他对象一样被初始化和垃圾收
  • 为什么连接终止

    我正在尝试使用随机森林分类模型H2OR 内部的库 训练集有 7000 万行和 25 个数字特征 总文件大小为 5 6 GB 验证文件的大小为 1 GB 我的系统有 16 GB RAM 和 8 核 CPU 系统成功读取 H2O 对象中的两个文
  • JVM 最大堆大小可以是动态的吗?

    JVM Xmx 参数允许将 JVM 的最大堆大小设置为某个值 但是 有没有办法让这个价值动态化呢 换句话说 我想告诉 JVM 看 如果你需要它 就继续从系统中获取 RAM 直到系统退出 提问原因分为两部分 首先 所讨论的应用程序可以根据用户
  • “boost does it”在多大程度上等同于“非常便携,用它”?

    In 这个答案 https stackoverflow com questions 2333321 most portable and reliable way to get the address of variable in c 233
  • 增强缓冲区调用后丢失自定义点类型的数据

    我有我自己的观点 class LocationWayPoint public latlong container location WORD index PWeakBasicStation station namespace boost n
  • 限制 Java 进程的总内存消耗(在 Cloud Foundry 中)

    与这两个问题相关 如何设置JVM的最大内存使用量 https stackoverflow com questions 1493913 how to set the maximum memory usage for jvm 什么会导致 jav
  • Python列表内存存储[重复]

    这个问题在这里已经有答案了 据我了解 Python 列表本质上是 C 数组 它们分配特定的顺序内存块 但是 这些内存块实际上存储列表中的数据还是它们只是指向内存中存储实际数据的另一个位置 它可能取决于列表中存储的对象的大小吗 因为您可以轻松
  • 将 boost::assign 与嵌套在 std::map 内的 std::set 一起使用

    我正在尝试使用boost assign模拟 C 11 的初始化std map含有一个std set include
  • udp客户端服务器从拉模型更改为推模型[关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 我已经实施了udp client和 udp server 其中服务器和客户端遵循拉模型 服务器仅在客户端请求时推送数据
  • boost 是否有可移植的方式来使用 ntohl/htonl/ntohs/htons 类型函数?

    我正在使用 UDP 特别是 boost asio ip udp socket 套接字 如果有帮助的话 头文件是什么 我需要哪些标头 类来处理 UDP 提升下的网络字节排序 刚刚发现就足够了 include
  • std::deque 的内存开销到底是怎么回事?

    我正在研究一种使用外部排序算法std queue并且必须仔细限制其内存使用 我注意到在合并阶段 使用了几个std queues 固定长度 我的内存使用量增加到我预期的大约 2 5 倍 自从std queue默认情况下使用std deque作
  • 来自 boost Asio Deadline_timer 的多个 async_wait

    是否可以在同一个 boost asio deadline timer 上多次调用 async wait 我想做的事情如下 t gt expires from now delay t gt async wait f1 t gt async w
  • 如何使用boost库读取和写入.ini文件[重复]

    这个问题在这里已经有答案了 如何使用boost库读取和写入 或修改 ini文件 With Boost PropertyTree您可以读取并更新树 然后写入文件 请参阅load and save功能 看一下如何访问属性树中的数据 http w
  • gcc 没有小字符串优化吗?

    Most std string实现 包括 GCC 使用小字符串优化 例如 有一个answer https stackoverflow com a 21710033 2640636讨论这个 今天 我决定检查我编译的代码中的字符串在什么时候被移
  • 设置种子增强::随机

    我想通过使用不同的种子数来重置随机序列 运行此测试代码时 boost mt19937 gener 1 boost normal distribution lt gt normal 0 1 boost variate generator

随机推荐

  • Jetty Websocket 空闲超时

    我最近一直在研究带注释的 websocket Jetty API 9 4 5 release 并与它进行了聊天 但是我遇到了一个问题 5 分钟后 我相信这是默认计时器 会话关闭 这不是由于错误 我发现的唯一解决方案是在关闭事件时通知我的套接
  • 当我检查 nginx access.log 时,定期出现未知的 HEAD 请求

    首先我使用的服务器环境 服务器 nginx uwsgi django 应用程序 docker AWS ECS 部署 芹菜 rabbitmq ec2 缓存 redis ec2 日志记录 AWS CloudWatch 日志 瞭望塔第三方应用程序
  • 从 SwiftUI 中的 UIKit 更新 UIViewRepresentable 大小

    我正在嵌入一个高度可变的视图控制器UITextView在父 SwiftUI VStack 中 视图控制器将其框架调整为整个屏幕viewDidLoad and viewDidLayoutSubviews The UITextView仅扩展到其
  • PyCharm 中的 Python3 语法

    我想在 PyCharm 2016 2 中使用 python3 语法 并将解释器配置为使用 python3 5 当我执行代码时 代码运行良好 但 PyCharm 抱怨 python2 不支持 python3 语法 如何让 PyC harm 相
  • 预加载图像后触发事件

    这是我用来预加载图像的代码 我不确定它是否是最好的 我的问题是 如何触发和事件 例如alert 完成加载所有图像后的对话框 var preload a gif b gif c gif var images for i 0 i lt prel
  • 使用具有多列的 data.table 将宽形状重塑为长形状

    我有一个宽格式的数据框 如下所示 我想使用 data table Melt 函数将宽形状重塑为长形状 在简单的情况下 我可以拆分两个数据 然后绑定两个数据集 但就我而言 有多个 test i testgr i 列 但必须有更好 更有效的方法
  • 动态添加表单到 Django 表单集

    我想动态地将新表单添加到 Django 表单集 以便当用户单击 添加 按钮时 它会运行 JavaScript 将新表单 是表单集的一部分 添加到页面 这就是我的做法 使用jQuery 我的模板 h3 My Services h3 servi
  • 如何调查“尝试释放未引用的标量”

    Perl 脚本 使用大量本地编写的模块 并且正在积极开发中 刚刚开始生成零星的 尝试释放未引用的标量 SV 0xa6e685c Perl 解释器 全球毁灭期间的 0x96d9008 消息 这总是可重复的 从某种意义上说 特定的命令序列总是会
  • CVXPY 中的初始猜测/热启动:给出解决方案的提示

    在这段代码中 import cvxpy as cvx Examples linear programming Create two scalar optimization variables x cvx Variable y cvx Var
  • VBA Excel 错误处理 - 特别是在函数中 - 专业 Excel 开发风格

    我拿到了 Rob Bovey 写的 Professional Excel Development 一书 它让我大开眼界 我正在通过错误处理重新调整我的代码 然而 还有很多我不明白的地方 我特别需要知道如何在函数中正确使用它 我使用 Bove
  • 将 hashtable.Keys 转换为 List 或其他 IEnumerable

    我知道 我还有其他选择 例如我可以维护一个单独的密钥列表 请不要建议其他选择 我只是想知道我能否成功 请不要问我要解决什么问题或类似的问题 这是一道纯粹 简单的CS题 我想知道是否有人知道如何从Hashtable并将它们扔进List
  • 在 R 中按名称模式选择列表元素

    我有一个像这样的清单 places lt list cities 1 c Madrid Valencia Barcelona countries 1 c Espa a Portugal Francia cities 2 c Alicante
  • 使用 C++ 和 Cocoa 代替 Objective-C?

    我想编写使用 C 和 Cocoa 框架的应用程序 因为 Apple 不支持 Carbon 64 位 C 在 Linux 和 Windows 上的实现似乎相当普通 但在 Mac OS X 上似乎需要额外的 Apple 特定代码片段 如 Obj
  • 理解三维数组

    我正在尝试了解三维数组 我知道它们是二维数组的数组 但是我正在读的书说了一些让我困惑的事情 在我正在阅读的书的练习中 它要求我为全彩图像创建一个三维数组 它给出了一个小例子 如果我们决定选择三维数组 则数组的声明方式如下 int color
  • 是否可以将 AutoLayout 与 UITableView 的 tableHeaderView 一起使用? [复制]

    这个问题在这里已经有答案了 自从我发现AutoLayout我到处都使用它 现在我尝试将它与tableHeaderView 我制造了一个subclass of UIView添加了我想要的所有内容 标签等 及其约束 然后我添加了这个Custom
  • 服务器端事件 (SSE) 未到达客户端

    我正在使用 MEAN 并尝试从服务器端接收事件 为此 我使用 EventSource 但它不起作用 我看到连接是如何打开的 但我没有从服务器收到任何消息 我可以在节点控制台中看到消息是如何发送的 但在客户端没有任何内容 浏览器控制台 我有点
  • 保存混乱的 .jsx 文件时采用更漂亮的格式

    I have format on save启用后 它在 js 文件中工作正常 但是当我尝试将代码保存在 jsx 文件中时 一切都会变得混乱 如何配置我的 prettier 以避免此问题而不禁用format on save 如果您使用的是 V
  • 自动映射器异常:“缺少类型映射配置或不支持的映射。”

    我尝试在 ASP NET MVC 5 应用程序中使用 Ninject 该应用程序使用 AutoMapper 将模型映射到视图模型 反之亦然 不幸的是 我收到一条错误消息 指出缺少类型映射配置 我创建了一个 Ninject 依赖解析器 nam
  • 如何以编程方式设置 dp 中的 EditText 上边距?

    在我的 Android 应用程序中 我想更改 editText 的 topMargin 问题是我想改变它 dp 明智而不是像素明智 我只想更改 topMaring 让另一个保持原样 不将它们设置为零 以编程方式我只能在 int 中设置边距
  • 提升无锁 spsc_queue 缓存内存访问

    我需要非常关注当前多线程项目中的速度 延迟 我试图更好地理解缓存访问 我不清楚无锁队列 例如 boost lockfree spsc queue 如何在缓存级别访问 使用内存 我见过使用队列 其中需要由消费者核心操作的大对象的指针被推入队列