0

我有一个多线程服务器 C++ 程序,它使用 MSXML6 并不断解析 XML 消息,然后应用准备好的 XSLT 转换来生成文本。我在具有 4 个 CPU 的服务器上运行它。每个线程都是完全独立的,并使用自己的变换对象。线程之间不共享任何 COM 对象。

这很好用,但问题是可扩展性。运行时:

  1. 使用一个线程,每个线程每秒可以进行大约 26 次解析+转换。
  2. 有 2 个线程,我得到大约 20/s/线程,
  3. 带 3 个线程,18/s/线程。
  4. 有 4 个线程,15/s/线程。

由于线程之间没有共享任何东西,我期望接近线性的可伸缩性,因此 4 线程应该比 1 快 4 倍。相反,它只快 2.3 倍。

它看起来像一个经典的争用问题。我编写了测试程序来消除我的代码中存在争用的可能性。我正在使用 DOMDocument60 类而不是 FreeThreadedDOMDocument 类,以避免不必要的锁定,因为文档永远不会在线程之间共享。我努力寻找缓存行错误共享的任何证据,但至少在我的代码中没有。

另一个线索,每个线程的上下文切换速率 > 15k/s。我猜罪魁祸首是 COM 内存管理器或 MSXML 中的内存管理器。也许它有一个全局锁,必须为每个内存分配/释放获取和释放。我简直不敢相信,在这个时代,内存管理器的编写方式并不能很好地适应多线程多 CPU 场景。

有谁知道是什么导致了这种争用或如何消除它?

4

3 回答 3

2

基于堆的内存管理器(您的基本 malloc/free)使用单个互斥锁是相当普遍的,这有相当充分的理由:堆内存区域是一个单一的连贯数据结构。

有一些替代的内存管理策略(例如分层分配器)没有这个限制。您应该调查自定义 MSXML 使用的分配器。

或者,您应该研究从多线程体系结构迁移到多进程体系结构,每个 MSXML 工作程序具有单独的进程。由于您的 MSXML 工作程序将字符串数据作为输入和输出,因此您没有序列化问题。

总结:使用多进程架构,它更适合您的问题,并且可以更好地扩展。

于 2008-11-28T19:20:25.343 回答
1

MSXML uses BSTRs, which use a global lock in its heap management. It caused us a ton of trouble for a massively multiuser app a few years ago.

We removed our use of XML in our app, you may not be able to do this, so you might be better off using an alternative XML parser.

于 2008-11-29T00:10:39.893 回答
1

Thanks for the answers. I ended up implementing a mix of the two suggestions.

I made a COM+ ServicedComponent in C#, hosted it as a separate server process under COM+, and used the XSLCompiledTransform to run the transformation. The C++ server connects to this external process using COM and sends it the XML and gets back the transformed string. This doubled the performance.

于 2009-02-09T16:30:28.603 回答