在一个具有 2Tb DRAM 的 80 核(160HT)nehalem 架构上运行一些测试后,我遇到一个小小的 HPC 问题: 当每个线程开始请求“错误”套接字上的对象的信息时,具有 2 个以上套接字的服务器开始大量停滞(延迟),即请求从在一个套接字上处理某些对象到实际上获取位于另一个套接字上的 DRAM 中的信息的线程。 即使我知道它们正在等待远程套接字返回请求,内核也显示 100% 利用率。 由于大多数代码都是异步运行的,因此重写代码以便我只解析从一个套接字上的线程到另一个套接字上的消息(无锁定等待)要容易得多。 此外,我想将每个线程锁定到内存池,以便我可以更新对象,而不是在垃圾收集器上浪费时间(约 30%)。 因此问题是: 如何在 Python 中将线程固定到具有预定内存池对象的内核上? 再提供一些背景信息: 当你在中间放置 ZeroMQ 并将消息传递作为每个 ZMQworker 管理的内存池之间的传递时,Python 无问题地运行多核。在 ZMQ 的 8M 消息/秒下,对象的内部更新所需的时间比管道填充所需的时间长。所有这些都此处进行了描述:zguide.zeromq.org/page:all#Ch… 所以,经过一点过大的简化,我生成了 80 个 ZMQworker 进程和 1 个 ZMQ 路由器,并在上下文中加载了大量对象(实际上是 5.84 亿个对象)。 从这个“起点”开始,对象需要进行交互以完成计算。 这是一个想法: *如果“对象 X”需要与“对象 Y”进行交互并且在 Python 线程的本地内存池中可用,那么应该直接进行交互。 如果“对象 Y”不在同一个池中可用,那么我希望它通过 ZMQ 路由器发送消息,并让路由器在稍后的某个时间点返回响应。我的架构是非阻塞的,因此特定 Python 线程中的内容只是继续执行,而无需等待 zmq 路由器的响应。即便是在同一套接字上但在不同内核上的对象,我也宁愿不进行交互,因为我更喜欢进行干净的消息交换,而不是让两个线程操作同一个内存对象。
为了做到这一点,我需要知道:
- 如何确定给定的 Python 进程(线程)在哪个套接字上运行。
- 如何将该特定套接字上的内存池分配给 Python 进程(一些 malloc 限制或类似的限制,以便一个套接字的内存池总和不会将内存池从一个套接字推送到另一个套接字)。
- 我没有想到的事情。
但我在 Python 文档中找不到如何做这件事的参考,在谷歌上我一定是搜索错了。 更新: 关于“为什么要在 MPI 架构上使用 ZeroMQ?”的问题,请阅读该主题:Spread vs MPI vs zeromq?因为我正在开发的应用程序被设计用于分布式部署,即使它是在 MPI 更合适的架构上测试的。 更新 2: 关于“如何在 Python(3) 中将线程固定到具有预定内存池的对象”的问题,答案在 psutils 中:
>>> import psutil
>>> psutil.cpu_count()
4
>>> p = psutil.Process()
>>> p.cpu_affinity() # get
[0, 1, 2, 3]
>>> p.cpu_affinity([0]) # set; from now on, this process will run on CPU #0 only
>>> p.cpu_affinity()
[0]
>>>
>>> # reset affinity against all CPUs
>>> all_cpus = list(range(psutil.cpu_count()))
>>> p.cpu_affinity(all_cpus)
>>>
工作进程可以固定到内核上,从而可以有效地利用 NUMA(查找您的 CPU 类型以验证它是否为 NUMA 架构!)。 第二个元素是确定内存池。可以使用 psutils 或 resource 库来完成此操作:
2. 解决方案
您可能低估了这个问题,没有非常简单的方法来完成您想要的。作为一般准则,您需要在操作系统级别上工作以便将事情按照您想要的方式设置。您想要使用所谓的“CPU 关联性”和“内存关联性”,并且您需要好好考虑您的系统架构以及软件架构才能正确完成。在真正的 HPC 中,命名的“关联性”通常由 MPI 库(例如 Open MPI)处理。您可能需要考虑使用一个并让您的不同进程由该 MPI 库处理。操作系统、MPI 库和 Python 之间的接口可以由 mpi4py 包提供。
您还需要将您的线程、进程和操作系统的概念理清。虽然对于 CPU 时间调度程序而言,线程是一个要调度的任务,因此理论上可以具有单个关联性,但我只知道整个进程(即一个进程中的所有线程)的关联性掩码。为了控制内存访问,NUMA(非一致内存访问)是正确的关键字,您可能想要查看 linuxmanpages.com/man8/numact… 在任何情况下,您都需要阅读有关关联性主题的文章,并且可能需要开始阅读 Open MPI 常见问题解答中的 CPU/内存关联性:www.open-mpi.de/faq/?catego… 如果您想在不使用 MPI 库的情况下实现您的目标,请查看 Linux 发行版的 util-linux 或 schedutils 和 numactl 包,以便获得有用的命令行工具,例如 taskset,您可以从 Python 中调用它来设置某些进程 ID 的关联性掩码。 这篇文章似乎生动地描述了 MPI 库如何对您的问题有所帮助:blogs.cisco.com/performance… 此 SO 答案描述了如何剖析您的硬件架构:stackoverflow.com/a/11761943/… 一般来说,我想知道您应用的机器是否适合此任务,或者您是否在错误的地方进行优化。如果您在一个机器中进行消息传递并达到内存带宽限制,我不确定 ZMQ(通过 TCP/IP,对吧?)是否是执行消息传递的正确工具。回到 MPI,HPC 应用程序的消息传递接口……