Python 如何将线程固定到具有预定内存池对象的内核?(80核Nehalem体系结构2Tb RAM)
在使用2Tb DRAM对80core(160HT)nehalem体系结构运行一些测试后,我遇到了一个小HPC问题: 当每个线程开始请求有关“错误”套接字上的对象的信息时,具有2个以上套接字的服务器开始大量暂停(延迟),即请求从一个正在处理一个套接字上的某些对象的线程转到另一个套接字上的DRAM中 尽管我知道它们正在等待远程套接字返回请求,但核心似乎已被100%利用 由于大多数代码都是异步运行的,因此重写代码要容易得多,这样我就可以将消息从一个套接字上的线程解析到另一个套接字上的线程(无需锁定等待)。 此外,我希望将每个线程锁定到内存池,这样我就可以更新对象,而不是在垃圾收集器上浪费时间(~30%) 因此,问题是: 如何在Python中将线程固定到具有预定内存池对象的内核? 更详细一点:Python 如何将线程固定到具有预定内存池对象的内核?(80核Nehalem体系结构2Tb RAM),python,multithreading,threadpool,hpc,ipython-parallel,Python,Multithreading,Threadpool,Hpc,Ipython Parallel,在使用2Tb DRAM对80core(160HT)nehalem体系结构运行一些测试后,我遇到了一个小HPC问题: 当每个线程开始请求有关“错误”套接字上的对象的信息时,具有2个以上套接字的服务器开始大量暂停(延迟),即请求从一个正在处理一个套接字上的某些对象的线程转到另一个套接字上的DRAM中 尽管我知道它们正在等待远程套接字返回请求,但核心似乎已被100%利用 由于大多数代码都是异步运行的,因此重写代码要容易得多,这样我就可以将消息从一个套接字上的线程解析到另一个套接字上的线程(无需锁定等待
Python在将ZeOMQ放在中间时,并在每个ZMQWork所管理的内存池之间传递消息时,不存在运行多核的问题。在ZMQ的8M msg/秒的速度下,对象的内部更新所花费的时间比管道所能填充的时间要长。这一切都在这里描述:
因此,稍微简化一下,我生成了80个zmqworkerprocess和1个ZMQrouter,并用大量对象(实际上是5.84亿个对象)加载上下文。 从这个“起点”开始,对象需要交互以完成计算 这就是想法:- 如果“对象X”需要与“对象Y”交互并且在中可用 python线程的本地内存池,然后是交互 应该直接做李>
- 如果“对象Y”在同一池中不可用,则我希望它可用 通过ZMQrouter发送消息,并让路由器返回 在稍后的某个时间点做出响应。我的体系结构是非阻塞的,因此在特定python线程中发生的事情只会继续,而不必等待zmqRouters响应。即使对于同一套接字上但位于不同核心上的对象,我也不希望进行交互,因为我更希望使用干净的消息交换,而不是让两个线程操作同一个内存对象
第二个元素是确定内存池。这也可以通过:完成,只是想知道这是否适合使用python远程对象-这可能值得研究,但不幸的是,我没有访问此类硬件的权限 如中所述,虽然pyro通常用于在网络上的多台机器之间分配工作,但它也可用于在一台机器上的内核之间共享处理 在较低级别上,Pyro只是进程间通信的一种形式。因此,无论您在Python组件之间都使用了更原始的IPC(如普通TCP/IP套接字),您可以考虑使用pyro代替。
虽然pyro可能会增加一些开销,但它很可能会加快速度,并使事情更易于维护。您可能低估了这个问题,但要实现您的目标,没有超简单的方法。一般来说,您需要在操作系统级别工作,以便按照您想要的方式进行设置。您希望使用所谓的“CPU相关性”和“内存相关性”,并且您需要认真考虑您的系统体系结构以及您的软件体系结构,以确保一切正常。在真正的HPC中,命名的“亲缘关系”通常由MPI库(如Open MPI)处理。您可能需要考虑使用其中一个,并让不同的进程由MPI库处理。mpi4py包可以提供操作系统、MPI库和Python之间的接口 您还需要了解线程和进程的概念以及操作系统设置。对于CPU时间调度器,线程是要调度的任务,因此理论上可以有单独的关联,但我只知道整个进程的关联掩码,即一个进程内的所有线程。对于控制内存访问,NUMA(非统一内存访问)是正确的关键字,您可能需要研究它 在任何情况下,您都需要阅读有关关联主题的文章,并且可能希望开始阅读有关CPU/内存关联的Open MPI常见问题解答: 如果您想在不使用MPI库的情况下实现目标,请查看linux发行版的包
util linux
或schedutils
和numactl
,以获得有用的命令行工具,如任务集
,例如,您可以在Python中调用它,以便为某些进程ID设置关联掩码
>>> 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)
>>>