1

使用 LINKED LIST 存储重叠结构是个好主意吗?

我的重叠结构看起来像这样

typedef struct _PER_IO_CONTEXT 
{

  WSAOVERLAPPED               Overlapped;
  WSABUF                      wsabuf;
  ....
  some other per operation data
  ....
  struct _PER_IO_CONTEXT      *pOverlappedForward;
  struct _PER_IO_CONTEXT      *pOverlappedBack;

 } PER_IO_CONTEXT, *PPER_IO_CONTEXT;

当 iocp 服务器启动时,我在链接列表中分配(例如)其中的 5000 个。此列表的开头存储在全局变量 PPER_IO_CONTEXT OvlList 中。我必须补充一点,我只在必须向所有连接的客户端发送数据的情况下使用这个链表。当 Wsasend 发布或 GQCS 收到通知时,更新链表(我使用 EnterCriticalSection 进行同步)。提前感谢您对更好的存储(缓存)重叠结构的提示、意见和建议。

4

1 回答 1

3

我假设建议的用例是您希望缓存“每个操作”重叠结构以避免重复分配和释放动态内存,这可能导致分配器争用和堆碎片。

使用单个“池”可以减少“使用用于分配和销毁重叠结构的分配器的所有线程之间的争用”到“所有发出或处理 I/O 操作的线程之间的争用”,这通常是一件好事去做。你是对的,你需要围绕访问进行同步,一个关键部分或独占模式下的 SRW 锁可能是最好的(后者对于非竞争访问来说要快一点)。

在长时间运行的系统中,减少堆碎片也是值得的。

使用“标准”的非侵入式列表,例如std::deque起初看起来是显而易见的选择,但非侵入式集合的问题在于它们倾向于为每个操作分配和取消分配内存(所以你又回到了原来的争用)。恕我直言,最好在每个重叠的结构中放置一个指针并将它们简单地链接在一起。这不需要在访问池时分配或释放额外的内存,这意味着您的争用又回到了使用池的线程上。

就我个人而言,我发现我只需要一个池(空闲列表)的每个操作结构的单链表,它实际上只是一个堆栈,如果我想维护一个“正在使用”的列表,则需要一个双链表。有时有用的操作数据(虽然不是我现在做的事情)。

下一步可能是拥有多个池,但这取决于系统的设计和 I/O 的工作方式。

如果您可以对给定的连接进行多个挂起的发送和接收操作,那么在连接级别拥有一个小池可能会很有用。这可以显着减少单个共享池的争用,因为每个连接首先会尝试使用每个连接池,如果该池为空(或已满)则回退到使用全局池。这往往会导致对全局池锁的争用要少得多。

于 2013-12-31T12:50:49.047 回答