NUMA硬件上的内存分配与访问
我正在用Python开发一个科学计算工具,这个工具需要能够在多个核心上分配工作,适用于NUMA共享内存环境。我在寻找最有效的方法来实现这个目标。
由于Python的全局解释器锁,线程不太适合,所以我只能选择使用“分叉”(fork)。在进程间通信方面,我想我可以用管道、套接字或者共享内存(mmap)。如果我漏掉了什么,请告诉我。
我的应用需要进程之间进行大量的通信,并且访问一些共享的数据。我最关心的是延迟问题。
我的问题是:当我分叉一个进程时,它的内存会被分配到靠近它所分配的核心吗?因为在类Unix系统中,分叉是“写时复制”,所以我想一开始可能不是这样。我是否需要强制复制以加快内存访问速度?如果是的话,最好的方法是什么?如果我使用mmap进行通信,这块内存是否仍然可以分布在多个核心上,还是只会在一个核心上?有没有一种方法可以透明地重新定位数据以优化访问?有没有办法直接控制物理内存的分配,或者请求有关分配的信息以帮助优化?
从更高的层面来看,这些事情中哪些是由硬件决定的,哪些是由操作系统决定的?我正在考虑购买一台高端的多插槽机器,现在在AMD Opteron和Intel Xeon之间犹豫。具体硬件对以上问题有什么影响?
1 个回答
Python有一个叫GIL的限制,所以它在多进程方面的支持更好。比如说,有队列、管道、锁、共享值和共享数组等工具。还有一个叫做Manager的东西,可以让你把很多Python的数据结构包装起来,以一种适合进程间通信的方式共享。我想这些大多数是通过管道或套接字来工作的,但我没有深入研究过内部机制。
http://docs.python.org/2/library/multiprocessing.html
Linux是如何建模NUMA系统的?
内核会检测它运行在一个多核机器上,然后识别硬件的数量和拓扑结构。接着,它会用节点的概念创建这个拓扑的模型。节点是一个物理插槽,里面有一个CPU(可能有多个核心)和连接到它的内存。为什么是基于节点而不是核心呢?因为内存总线是连接RAM和CPU插槽的物理线路,单个插槽上的所有核心对该内存总线上的RAM都有相同的访问时间。
一个内存总线上的内存是如何被另一个内存总线上的核心访问的?
在x86系统上,这是通过缓存来实现的。现代操作系统使用一个叫做翻译后备缓冲区(TLB)的硬件,将虚拟地址映射到物理地址。如果缓存需要获取的内存是本地的,它就会在本地读取。如果不是本地的,它会通过AMD系统的Hyper Transport总线或Intel的QuickPath总线访问远程内存。因为这是在缓存级别完成的,理论上你不需要了解这些细节。而且你也无法控制它。但对于高性能应用来说,理解这些内容非常有用,可以减少远程访问的次数。
操作系统实际是如何定位虚拟内存的物理页面的?
当一个进程被复制时,它会继承所有父进程的页面(这叫做写时复制)。内核会知道哪个节点对这个进程是“最佳”的,也就是它的“首选”节点。这个设置可以修改,但默认情况下会和父进程相同。内存分配默认会在和父进程相同的节点上进行,除非明确更改。
有没有透明的过程来移动内存?
没有。一旦内存被分配,它就固定在分配时的节点上。你可以在另一个节点上进行新的分配,移动数据,然后在第一个节点上释放内存,但这有点麻烦。
有没有办法控制内存分配?
默认情况下是分配到本地节点。如果你使用libnuma,你可以改变分配的方式(比如轮询或交错),而不是默认的本地分配。
我从这篇博客文章中获取了很多信息:
http://blog.jcole.us/2010/09/28/mysql-swap-insanity-and-the-numa-architecture/
我强烈建议你完整阅读它,以获取更多信息。