良好的跨线程分配和免费分配分配器 我计划编写一个C++网络应用程序,其中: 我使用一个线程来接受TCP连接并从中读取数据。我计划使用epoll/select来完成此操作。数据被写入缓冲区,缓冲区是使用一些arena分配器(比如jemalloc)分配的 一旦来自单个TCP客户机的数据足够形成协议消息,数据就会发布到环形缓冲区中。环形缓冲区结构包含连接的fd和指向包含相关数据的缓冲区的指针 工作线程处理来自环形缓冲区的条目,并向客户端发送一些结果数据。在处理每个事件之后,工作线程释放实际的数据缓冲区,将其返回给arena分配器以供重用

良好的跨线程分配和免费分配分配器 我计划编写一个C++网络应用程序,其中: 我使用一个线程来接受TCP连接并从中读取数据。我计划使用epoll/select来完成此操作。数据被写入缓冲区,缓冲区是使用一些arena分配器(比如jemalloc)分配的 一旦来自单个TCP客户机的数据足够形成协议消息,数据就会发布到环形缓冲区中。环形缓冲区结构包含连接的fd和指向包含相关数据的缓冲区的指针 工作线程处理来自环形缓冲区的条目,并向客户端发送一些结果数据。在处理每个事件之后,工作线程释放实际的数据缓冲区,将其返回给arena分配器以供重用,c++,multithreading,memory-management,C++,Multithreading,Memory Management,我省略了发布者如何使其写入的数据对工作线程可见的细节 所以我的问题是:有没有为这种行为(即在一个线程上分配对象,在另一个线程上释放对象)进行优化的分配器 我特别担心的是,必须使用锁将内存返回到一个不是线程亲缘竞技场的竞技场。我还担心错误共享,因为生产者线程和工作者线程都将写入同一个区域。似乎jemalloc或tcmalloc都没有为此进行优化。在为多线程应用程序实现高度优化的分配器之前,您应该首先使用标准的new和delete操作符来实现。在正确实现应用程序之后,您可以着手解决通过分析应用程序发

我省略了发布者如何使其写入的数据对工作线程可见的细节

所以我的问题是:有没有为这种行为(即在一个线程上分配对象,在另一个线程上释放对象)进行优化的分配器


我特别担心的是,必须使用锁将内存返回到一个不是线程亲缘竞技场的竞技场。我还担心错误共享,因为生产者线程和工作者线程都将写入同一个区域。似乎jemalloc或tcmalloc都没有为此进行优化。

在为多线程应用程序实现高度优化的分配器之前,您应该首先使用标准的
new
delete
操作符来实现。在正确实现应用程序之后,您可以着手解决通过分析应用程序发现的瓶颈问题

如果到了标准
new
delete
分配器显然是应用程序的瓶颈的阶段,我使用的方法如下:

假设:线程的数量是固定的,并且是静态创建的

  • 每个线程都有自己的竞技场
  • 从竞技场获取的每个对象都有一个指向其来源竞技场的引用
  • 每个竞技场的每个线程都有一个单独的垃圾列表
  • 当一个线程释放一个对象时,它会回到它所来自的竞技场,但会被放入特定于线程的垃圾列表中
  • 实际拥有竞技场的线程将其垃圾列表视为真正的空闲列表
  • 拥有竞技场的线程定期执行垃圾收集过程,将其他线程垃圾列表中的对象折叠到真正的空闲列表中

“定期”垃圾收集通行证不一定是基于时间的。例如,在每次分配时,垃圾的一个子集可能会被回收并释放。

处理内存分配和释放问题的最佳方法是不处理它

你提到了一个环形缓冲区。这些通常是固定尺寸的。如果您能为您的协议消息提供一个固定的最大大小,那么您就可以在程序启动时分配所需的所有内存。解除分配时,保留内存,但将其重置为新状态

现在,您的程序在处理每条消息时可能需要分配和取消分配内存,但这将在每个线程中完成,跨线程问题将不会起作用


如果您可以分配大多数消息将使用的内存量,并且在必要时具有分配更多内存的处理程序,则即使消息的最大大小太大而无法预分配,这也可以工作。

如果所有线程的行为都对称,则此项工作正常。但是,如果设计是这样的,即某些线程总是分配某些类型,而这些类型总是被从不分配这些类型的线程释放,那么这种技术将使程序表现得像是在泄漏内存。谢谢你的建议。我的应用程序处理各种大小的图像。我的环形缓冲区预先分配了你提到的所有对象。这样就不必创建新对象并释放它们。除此状态外,环形缓冲区条目还包含指向包含图像的缓冲区的指针。无法预分配这些缓冲区,因为最大大小限制太高。谢谢您的回答。你的假设是正确的。我一开始就启动了所有线程,它们的数量永远不会改变。这似乎是一件值得尝试的好事情。正如你所说,我还没有做任何基准测试。在我开始优化任何东西之前,我肯定会这样做。锁争用不应该是一个太大的问题。与处理图像数据所需的时间相比,必须持有锁的时间非常少——只足以将小的“fd+buffer-pointer”结构推送到您使用的任何队列/容器上。将它们返回到某个池中重复使用也是一样。@MartinJames:这个问题本身比询问者的用例具有更广泛的适用性。我已经警告询问者不要尝试实现如此复杂的东西,除非应用程序被证明需要它。其他人的应用程序可能需要类似的东西,他们的搜索可能会将他们带到这里。@MartinJames。与实际的图像处理相比,在锁较小的情况下,将缓冲区返回池所需的时间可能是正确的。在最初的问题中,我故意没有提到图像处理,并且对协议消息的内容含糊不清,以了解解决方案,以防出现问题。此外,我没有使用锁来更改或读取我的环形缓冲区结构。我使用的是放松原子,因为只有一个编写器。它允许内存由一个线程分配,由另一个线程释放,而不需要锁。