用于跨线程分配和免费的良好分配器

Posted

技术标签:

【中文标题】用于跨线程分配和免费的良好分配器【英文标题】:Good allocator for cross thread allocation and free 【发布时间】:2013-10-02 22:38:28 【问题描述】:

我打算编写一个 C++ 网络应用程序,其中:

    我使用单线程来接受 TCP 连接并从中读取数据。我打算使用 epoll/select 来做到这一点。数据被写入使用一些竞技场分配器分配的缓冲区,比如 jemalloc。 一旦来自单个 TCP 客户端的数据足以形成协议消息,数据就会发布到环形缓冲区。环形缓冲区结构包含连接的 fd 和指向包含相关数据的缓冲区的指针。 工作线程处理来自环形缓冲区的条目并将一些结果数据发送到客户端。处理完每个事件后,工作线程释放实际的数据缓冲区,将其返回给 arena 分配器以供重复使用。

我将省略有关发布者如何使其写入的数据对工作线程可见的详细信息。

所以我的问题是:是否有任何分配器针对这种行为进行优化,即在一个线程上分配对象并在另一个线程上释放?

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

【问题讨论】:

【参考方案1】:

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

如果您到了很明显标准的newdelete 分配器是应用程序的瓶颈的阶段,以下是我使用的方法:

假设:线程数是固定的,是静态创建的。

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

“定期”垃圾收集过程不一定要基于时间。例如,可以在每次分配时回收并释放一部分垃圾。

【讨论】:

感谢您的回答。你的假设是正确的。我从一开始就开始所有线程,它们的数量永远不会改变。这似乎是一个很好的尝试。就像你注意到的那样,我没有对任何东西进行基准测试。在我开始优化任何东西之前,我肯定会这样做。 锁争用应该不是太大的问题。与处理图像数据所用的时间相比,必须保持锁定的时间非常短——足以将小的“fd+缓冲区指针”结构推送到您使用的任何队列/容器上。与将它们返回某个池以重复使用相同。 @MartinJames:这个问题本身比提问者的用例具有更广泛的适用性。我已经警告过提问者不要尝试实现如此复杂的东西,除非应用程序被证明需要它。其他人的应用程序可能需要这样的东西,他们的搜索可能会将他们带到这里。 @MartinJames。与实际图像处理相比,将缓冲区返回到锁定池所需的时间可能是正确的。我故意在原始问题中没有提及图像处理,并且对协议消息的内容含糊不清,以了解解决方案,以防出现问题。此外,我没有使用锁来更改或读取我的环形缓冲区结构。我使用的是宽松的原子,因为只有一个作家。 @bop 就是允许一个线程分配内存,不加锁地被另一个线程释放。【参考方案2】:

处理内存分配和释放问题的最好方法是不去处理。

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

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

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

【讨论】:

如果所有线程的行为都是对称的,这将很有效。但是,如果设计是这样的,某些线程总是分配某些类型,而这些类型总是被从不分配这些类型的线程释放,那么这种技术将使程序表现得像它正在泄漏内存。 感谢您的建议。我的应用程序处理不同大小的图像。我的环形缓冲区会像您提到的那样预先分配所有对象。这照顾到不必创建新对象并释放它们。除此状态外,环形缓冲区条目还包含指向包含图像的缓冲区的指针。这些缓冲区无法预先分配,因为最大大小限制太高了。

以上是关于用于跨线程分配和免费的良好分配器的主要内容,如果未能解决你的问题,请参考以下文章

尝试在 Windows Phone 上显示相机胶卷中的图像时出现跨线程异常

跨线程共享内存访问

WPF怎么跨线程访问UI控件

c# 跨线程访问窗体UI

Visual C++ 跨线程发送消息

跨线程操作Treeview