我正在开发python中的科学计算工具,它应该能够在NUMA共享内存环境中的多个核心上分配工作。我正在研究最有效的方法。
由于python的全局解释器锁,线程很可能不在游戏中,这使得fork成为我唯一的选择。对于进程间通信,我想我的选项是管道,套接字或mmap。如果此列表中缺少某些内容,请指出。
我的应用程序需要在进程之间进行一些通信,并访问一些常见数据。我主要担心的是延迟。
我的问题:当我分叉一个进程时,它的内存是否会位于分配给它的核心附近?由于* nix中的fork在写入时会复制,所以最初我认为情况并非如此。我是否想强制复制以获得更快的内存访问权限,如果是这样,那么最好的方法是什么?如果我使用mmap进行通信,那么该内存是否仍然可以分布在内核上,还是位于单个内核中?是否有一个透明地重定位数据以优化访问的进程?有没有办法直接控制物理分配,或者有办法请求有关分配的信息以帮助优化?
在更高的层面上,这些东西中的哪一个是由我的硬件和操作系统决定的?我正在购买高端多机插件机,并怀疑AMD Opteron和Intel Xeon。具体硬件对上述任何问题的影响是什么?
答案 0 :(得分:2)
由于Python的跟腱之一是GIL,因此有更好的多进程支持。例如,有队列,管道,锁,共享值和共享阵列。还有一种称为管理器的东西,它允许您包装许多Python数据结构并以IPC友好的方式共享它们。我想这些大多数是通过管道或插座工作的,但我没有深入研究内部。
http://docs.python.org/2/library/multiprocessing.html
内核检测到它在多核计算机上运行,然后检测出有多少硬件和拓扑结构。然后,它使用节点的概念创建此拓扑的模型。 Node是一个物理套接字,它包含一个CPU(可能有多个内核)和连接到它的内存。为什么基于Node而不是基于核心?因为内存总线是将RAM连接到CPU插槽的物理线路,并且单个插槽中CPU上的所有内核对驻留在该内存总线上的所有RAM具有相同的访问时间。
在x86系统上,这是通过缓存。现代操作系统使用称为Translation Lookaside Buffer(TLB)的硬件将虚拟地址映射到物理地址。如果缓存已被赋予任务的内存是本地的,则它在本地读取。如果它不是本地的,它将通过AMD系统上的Hyper Transport总线或Intel上的QuickPath到远程内存来满足。因为它是在缓存级别完成的,所以理论上你不需要知道它。你肯定无法控制它。但是对于高性能应用程序来说,理解最小化远程访问量是非常有用的。
当一个进程被分叉时,它继承了它所有父进程的页面(由于COW)。内核知道哪个节点对于进程是“最佳”的,这是它的“首选”节点。这可以修改,但同样默认为与父项相同。内存分配将默认为与父节点相同的节点,除非它已明确更改。
没有。一旦分配了内存,它就固定在分配的节点上。您可以在另一个节点上进行新的分配,移动数据,并在第一个节点上取消分配,但这有点麻烦。
默认是分配给本地节点。如果您使用libnuma,您可以更改分配的方式(例如循环或交错),而不是默认为本地。
我从这篇博客文章中获取了大量信息:
http://blog.jcole.us/2010/09/28/mysql-swap-insanity-and-the-numa-architecture/
我肯定会建议您完整地阅读它以收集更多信息。
答案 1 :(得分:-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()