7

我正在用 python 开发一个科学计算工具,它应该能够在 NUMA 共享内存环境中的多个内核上分配工作。我正在研究最有效的方法。

由于 python 的全局解释器锁,线程 - 不幸地 - 退出了游戏,这使得 fork 成为我唯一的选择。对于进程间通信,我想我的选择是管道、套接字或 mmap。如果此列表中缺少某些内容,请指出。

我的应用程序需要在进程之间进行相当多的通信,并访问一些公共数据。我主要担心的是延迟。

我的问题:当我分叉一个进程时,它的内存是否会位于分配给它的核心附近?作为 *nix 副本中的 fork,最初我认为情况并非如此。我是否要强制复制以更快地访问内存,如果是这样,最好的方法是什么?如果我使用 mmap 进行通信,该内存是否仍可以分布在内核上,还是位于单个内核上?是否有透明地重新定位数据以优化访问的流程?有没有办法直接控制物理分配,或者请求有关分配的信息以帮助优化?

在更高的层次上,哪些是我的硬件决定的,哪些是由操作系统决定的?我正在购买一台高端多路机器,并在 AMD Opteron 和 Intel Xeon 之间犹豫不决。特定硬件对上述任何问题的影响是什么?

4

2 回答 2

4

由于 Python 的致命弱点之一是 GIL,因此有更好的多进程支持。例如有队列、管道、锁、共享值和共享数组。还有一种称为管理器的东西,它允许您包装大量 Python 数据结构并以 IPC 友好的方式共享它们。我想大多数这些都是通过管道或套接字工作的,但我并没有深入研究内部结构。

http://docs.python.org/2/library/multiprocessing.html

Linux 如何为 NUMA 系统建模?

内核检测到它在多核机器上运行,然后检测有多少硬件以及拓扑是什么。然后它使用节点的想法创建此拓扑的模型。节点是一个物理插槽,包含一个 CPU(可能具有多个内核)和连接到它的内存。为什么基于节点而不是基于核心?因为内存总线是将 RAM 连接到 CPU 插槽的物理线路,并且单个插槽中 CPU 上的所有内核对驻留在该内存总线上的所有 RAM 具有相同的访问时间。

一条内存总线上的内存如何被另一条内存总线上的内核访问?

在 x86 系统上,这是通过缓存。现代操作系统使用称为转换后备缓冲区 (TLB) 的硬件将虚拟地址映射到物理地址。如果缓存的任务是获取的内存是本地的,则它会在本地读取。如果它不是本地的,它将通过 AMD 系统上的 Hyper Transport 总线或 Intel 上的 QuickPath 到达远程内存以满足需求。由于它是在缓存级别完成的,因此理论上您不需要了解它。你当然无法控制它。但是对于高性能应用程序来说,理解最小化远程访问的数量是非常有用的。

操作系统实际上在哪里定位虚拟内存的物理页面?

当一个进程被分叉时,它会继承它的所有父页面(由于 COW)。内核知道哪个节点对于它是“首选”节点的进程是“最佳”的。这可以修改,但再次默认为与父级相同。内存分配将默认为与父节点相同的节点,除非它被显式更改。

是否存在移动记忆的透明过程?

不会。一旦分配了内存,它就会固定在分配它的节点上。您可以在另一个节点上进行新的分配,移动数据,然后在第一个节点上解除分配,但这有点麻烦。

有没有办法控制分配?

默认是分配给本地节点。如果您使用 libnuma,您可以更改分配的完成方式(例如循环或交错),而不是默认为本地。

我从这篇博文中获取了很多信息:

http://blog.jcole.us/2010/09/28/mysql-swap-insanity-and-the-numa-architecture/

我绝对建议您完整阅读它以收集更多信息。

于 2012-12-23T01:45:16.263 回答
-2
##client.py
import socket
import sys

HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])



sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:

    sock.connect((HOST, PORT))
    sock.sendall(bytes(data +"\n", "utf-8"))

    received = str(sock.recv(1024), "utf-8")
finally:
    sock.close()

print("Sent:     {}".format(data))
print("Received: {}".format(received))

##server.py
import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):

    def handle(self):

        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        self.request.sendall(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "10.0.0.1", 9999
    server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()
于 2015-03-24T15:48:25.660 回答