在多处理中,每个进程在 CPython 中都有自己的 GIL 是真的吗?这与创建新的运行时有何不同?
Is it true that in multiprocessing, each process gets it's own GIL in CPython? How different is that from creating new runtimes?
有什么注意事项吗?我有几个与之相关的问题。
创建更多 GIL 的成本是多少?它与创建单独的 python 运行时有什么不同吗?创建新的 GIL 后,它会根据该过程的需要从头开始创建所有内容(对象、变量、堆栈、堆),还是创建当前堆中所有内容的副本并创建堆栈? (如果垃圾收集在处理相同的对象时会发生故障。)正在执行的代码片段是否也被复制到新的 CPU 核心?我还可以将一个 GIL 与一个 CPU 核心联系起来吗?
现在复制东西是一项相当 CPU 密集的任务(如果我错了请纠正我),决定是否进行多处理的阈值是多少?
PS:我说的是 CPython,但请随意将答案扩展到您认为有必要的任何地方。
第一个标题问题的简短回答是:是的。每个进程都有自己的全局解释锁。在那之后,它变得复杂并且不是 Python 的问题,而是你的基础 OS.
的问题
在 Linux,通过 multiprocessing
生成新进程应该比启动新的 Python 解释器(从头开始)更便宜:
- 您
fork()
the parent process (side note: clone()
is actually used these days), the child already has your code and starts with a copy of parents address space -> since you are actually spawning another instance of your running process no need to execve()
(以及与之相关的所有开销)并重新填充其内容。
- 实际上当我们说复制地址space时,它实际上并没有全部复制,而是使用copy-on-write;所以除非你修改它,否则你根本不需要复制它。
出于这个原因,我的直觉是,多处理几乎总是比从头开始一个全新的 Python 解释器更有效。毕竟,即使你启动了一个新的解释器(大概来自 运行 进程),它首先执行 fork()
/clone()
包括 parent 地址的“复制” space 在进入 execve()
.
之前
但实际上这可能会有所不同,具体取决于您的底层 OS 如何处理新进程的创建及其内存管理。
时隔6个月再回头看这个问题,感觉可以解开年轻时的自己的疑惑了。我希望这对偶然发现它的人有所帮助。
是的,在 multiprocessing 模块中,每个进程都有一个单独的 GIL,对此没有任何注意事项。但是对runtime和GIL的理解有问题,需要更正。
我会用一系列的陈述来解惑/回答问题
- Python代码是由CPython虚拟机运行(编译成Cpython字节码然后解释这个字节码)。这就是 python 运行时的构成。
- 当我们创建一个新进程时,会启动一个全新的 python 虚拟机(我们称之为 python 进程),其中包含堆栈和堆内存。
- 是的,这是一个成本高昂的过程,但成本并不高。因为python虚拟机是一段预编译为机器码的C代码。换句话说,在 java 中他们不使用多处理的原因是它会创建多个 JVM,这会很糟糕,因为 JVM 需要大量内存,而且 JVM 不像 CPython.
- GIL 只是 python 虚拟机中的一段代码,它让 CPython 解释器只执行一行 CPython 字节码(或一条指令)时间。所以,所有与 GIL 创建和成本相关的问题都是愚蠢的。基本上目的是询问 CPython 虚拟机。
- 我可以将 1 个 GIL 关联到 1 个 CPU 内核吗? : 最好问一下 1 Python process 是否可以与 1 CPU core 相关? : 不。这是内核的工作来决定进程是什么核心 运行(并且会不时改变并且进程无法控制它)。唯一的问题是在任何给定时间点,一个 python 进程不能在多个内核上 运行 并且一个 python 进程将只执行 CPython 字节码中的一条指令(由于 GIL)。
在核心中复制了什么以及OS如何试图让一个进程保持它正在处理的核心本身是一个单独的非常深入的话题。
最后一个问题是一个主观问题,但根据所有这些理解,它基本上是一个成本效益比,可能因程序而异,并且可能取决于 CPU 进程的密集程度和内核数机器有没有等等所以不能一概而论
有什么注意事项吗?我有几个与之相关的问题。
创建更多 GIL 的成本是多少?它与创建单独的 python 运行时有什么不同吗?创建新的 GIL 后,它会根据该过程的需要从头开始创建所有内容(对象、变量、堆栈、堆),还是创建当前堆中所有内容的副本并创建堆栈? (如果垃圾收集在处理相同的对象时会发生故障。)正在执行的代码片段是否也被复制到新的 CPU 核心?我还可以将一个 GIL 与一个 CPU 核心联系起来吗?
现在复制东西是一项相当 CPU 密集的任务(如果我错了请纠正我),决定是否进行多处理的阈值是多少?
PS:我说的是 CPython,但请随意将答案扩展到您认为有必要的任何地方。
第一个标题问题的简短回答是:是的。每个进程都有自己的全局解释锁。在那之后,它变得复杂并且不是 Python 的问题,而是你的基础 OS.
的问题在 Linux,通过 multiprocessing
生成新进程应该比启动新的 Python 解释器(从头开始)更便宜:
- 您
fork()
the parent process (side note:clone()
is actually used these days), the child already has your code and starts with a copy of parents address space -> since you are actually spawning another instance of your running process no need toexecve()
(以及与之相关的所有开销)并重新填充其内容。 - 实际上当我们说复制地址space时,它实际上并没有全部复制,而是使用copy-on-write;所以除非你修改它,否则你根本不需要复制它。
出于这个原因,我的直觉是,多处理几乎总是比从头开始一个全新的 Python 解释器更有效。毕竟,即使你启动了一个新的解释器(大概来自 运行 进程),它首先执行 fork()
/clone()
包括 parent 地址的“复制” space 在进入 execve()
.
但实际上这可能会有所不同,具体取决于您的底层 OS 如何处理新进程的创建及其内存管理。
时隔6个月再回头看这个问题,感觉可以解开年轻时的自己的疑惑了。我希望这对偶然发现它的人有所帮助。
是的,在 multiprocessing 模块中,每个进程都有一个单独的 GIL,对此没有任何注意事项。但是对runtime和GIL的理解有问题,需要更正。
我会用一系列的陈述来解惑/回答问题
- Python代码是由CPython虚拟机运行(编译成Cpython字节码然后解释这个字节码)。这就是 python 运行时的构成。
- 当我们创建一个新进程时,会启动一个全新的 python 虚拟机(我们称之为 python 进程),其中包含堆栈和堆内存。
- 是的,这是一个成本高昂的过程,但成本并不高。因为python虚拟机是一段预编译为机器码的C代码。换句话说,在 java 中他们不使用多处理的原因是它会创建多个 JVM,这会很糟糕,因为 JVM 需要大量内存,而且 JVM 不像 CPython.
- GIL 只是 python 虚拟机中的一段代码,它让 CPython 解释器只执行一行 CPython 字节码(或一条指令)时间。所以,所有与 GIL 创建和成本相关的问题都是愚蠢的。基本上目的是询问 CPython 虚拟机。
- 我可以将 1 个 GIL 关联到 1 个 CPU 内核吗? : 最好问一下 1 Python process 是否可以与 1 CPU core 相关? : 不。这是内核的工作来决定进程是什么核心 运行(并且会不时改变并且进程无法控制它)。唯一的问题是在任何给定时间点,一个 python 进程不能在多个内核上 运行 并且一个 python 进程将只执行 CPython 字节码中的一条指令(由于 GIL)。
在核心中复制了什么以及OS如何试图让一个进程保持它正在处理的核心本身是一个单独的非常深入的话题。
最后一个问题是一个主观问题,但根据所有这些理解,它基本上是一个成本效益比,可能因程序而异,并且可能取决于 CPU 进程的密集程度和内核数机器有没有等等所以不能一概而论