我知道这个话题现在已经很老了,但由于它仍然是谷歌搜索“multiprocessing logical core”时的第一个答案......我觉得我必须给出一个额外的答案,因为我可以看到2018年甚至更晚的人们在这里很容易感到困惑(有些答案确实有点令人困惑)。
我认为这里没有比这更好的地方来警告读者一些上面的答案,所以抱歉再次将话题提起。
-->要计算CPU(逻辑/物理)请使用PSUTIL模块
对于一个4核心/8线程i7来说,它将返回:
import psutil
psutil.cpu_count(logical = False)
4
psutil.cpu_count(logical = True)
8
这就是如此简单。
您不必担心操作系统,平台,硬件或任何其他问题。 我确信它比multiprocessing.cpu_count()好得多,后者有时会给出奇怪的结果,据我个人经验。
--> 要使用N个物理核心(由您选择),请使用Yugi描述的multiprocessing模块。
只需计算您有多少个物理处理器,启动一个具有4个工作进程的multiprocessing.Pool。
或者还可以尝试使用joblib.Parallel()函数。
2018年的joblib不是Python标准分发的一部分,而只是multiprocessing模块的包装器,正如Yugi所述。
--> 大多数情况下,不要使用超过可用核心数量的核心(除非您已经针对非常特定的代码进行了基准测试并证明值得这样做)
关于“如果指定了超过可用核心数量的核心,操作系统将处理这些事情”的错误信息充斥着各处。 这是绝对错误的。 如果你使用比可用核心更多的核心,你将面临巨大的性能下降。 例外情况是如果工作进程是I / O限制的。 因为操作系统调度程序将尽其所能处理每个任务并分别工作,根据情况,在频繁切换的过程中最多可能花费100%的工作时间,这将是灾难性的。
不要只是相信我:试试它,进行基准测试,您会看到它是多么清晰明了。
--> 是否可以决定代码在逻辑核心还是物理核心上执行?
如果您问这个问题,则意味着您不了解物理和逻辑核心的设计方式,因此您可能应该更多地了解处理器的体系结构。
例如,如果您想在核心3而不是核心1上运行,则可能确实有一些解决方案,但仅当您知道如何编写操作系统的内核和调度程序时才可用,我认为如果您问这个问题就不是这种情况。
如果您在4个物理/8个逻辑处理器上启动4个CPU密集型进程,则调度程序将将每个进程分配给1个不同的物理核心(4个逻辑核心将保持未被使用/较差)。 但是,在4个逻辑/8个线程处理器上,如果处理单元是(0,1)(1,2)(2,3)(4,5)(5,6)(6,7),则无论进程在0号还是1号执行,都没有区别: 它是同一处理单元。
据我所知(但专家可能会确认,也许它与非常特定的硬件规格有所不同),在0或1上执行代码没有或几乎没有差异。在处理单元(0,1)中,我不确定0是逻辑还是物理,还是反之。从我的理解来看(可能是错误的),两者都是来自同一处理单元的处理器,它们只是共享它们的高速缓存/对硬件(包括RAM)的访问,并且0并不比1更是物理单位。
此外,您应该让操作系统决定。因为操作系统调度程序可以利用某些平台上存在的硬件逻辑内核加速(例如i7、i5、i3等),这是您无法控制的其他东西,而这可能真正对您有所帮助。
如果在4个物理/8个逻辑核心上启动5个CPU密集型任务,则行为将是混乱的,几乎是不可预测的,主要取决于您的硬件和操作系统。调度程序会尽力而为。几乎每次,您都会面临非常糟糕的性能。
假设我们仍然谈论的是4(8)经典架构:因为调度程序尽最大努力(因此经常切换属性),所以,根据您正在执行的进程,启动5个逻辑核心可能比启动8个逻辑核心更糟糕(在后者中,至少他知道一切将以100%使用,所以输了输了,他不会太努力避免它,不会经常切换,因此不会浪费太多时间)。
然而,几乎可以肯定地说(但要在硬件上进行基准测试以确保):如果使用超过可用物理核心的物理核心,则几乎任何多进程程序都会运行得更慢。
许多事情可能会干扰...... 程序、硬件、操作系统的状态、它使用的调度程序、您今天早晨吃的水果、您姐姐的名字... 如果您对某些事情持怀疑态度,只需进行基准测试即可,没有其他简单的方法来确定您是否失去了性能。有时计算机科学确实很奇怪。
--> 在Python中,大多数时间附加逻辑核是无用的(但并非总是如此)
在Python中进行真正并行任务的主要方法有两种。
- 多进程(无法利用逻辑核心)
- 多线程(可以利用逻辑核心)
例如,要并行运行4个任务
--> 多进程将创建4个不同的Python解释器。对于每个解释器,您必须启动一个Python解释器、定义读/写权限、定义环境、分配大量内存等。不妨直说:您将从0开始启动一个全新的程序实例。它可能需要大量时间,因此您必须确信这个新程序将工作足够长时间,以便它值得。
如果你的程序有足够多的工作量(至少几秒钟的工作时间),那么由于操作系统会将消耗CPU的进程分配到不同的物理核心上,程序可以快速运行并获得很高的性能表现,这是非常好的。由于操作系统几乎总是允许进程之间进行通信(虽然速度较慢),它们甚至可以交换一些数据。
--> 多线程编程则完全不同。在Python解释器内,它只会创建一个小的内存块,多个CPU可以同时共享和使用该内存块。创建线程比创建新进程要快得多(在旧电脑上创建新进程有时需要花费很长的时间,而创建线程只需要极短的时间)。您不需要创建新的进程,而是创建更轻的“线程”。
线程可以非常快速地在线程之间共享内存,因为它们实际上在同一块内存上同时操作(而在处理不同进程时,则必须复制/交换内存)。
但:为什么大多数情况下不能使用多线程?看起来非常方便?
在Python中有一个非常大的限制:在Python解释器中只能执行一条语句,这称为全局解释器锁(GIL)。因此,在大多数情况下,使用多线程甚至会降低性能,因为不同的线程必须等待访问相同的资源。对于纯计算处理(没有IO),多线程是无用的,如果您的代码是纯Python,则甚至更糟。但是,如果您的线程涉及任何等待IO,则多线程可能非常有益。
-->在使用多进程时,为什么不能使用逻辑核心?
逻辑内核没有自己的内存访问。它们只能在其所属物理处理器的内存访问和缓存上工作。例如,同一处理单元的逻辑核心和物理核心很可能同时在缓存内存的不同位置上使用相同的C/C++函数,从而使处理速度大大加快。
然而,这些是C/C++函数!Python是一个大型的C/C++封装器,需要比其等效的C++代码更多的内存和CPU。很可能在2018年,无论你想做什么,两个大的python进程将需要比单个物理+逻辑单元承受得起的更多的内存和缓存读写,并且比等效的C/C++真正多线程的代码消耗更多。这几乎总是会导致性能下降。请记住,每个不在处理器缓存中的变量,在内存中读取的时间将增加1000倍。如果你的缓存已经完全满了,强制使用2个进程,那么你猜会发生什么:它们将一次一个地使用它,永久切换,导致数据在每次切换时被愚蠢地刷新和重新读取。当数据从内存中读取或写入时,你可能认为你的CPU“正在”工作,但事实并非如此。它正在等待数据!什么都没做。
--> 那么你如何利用逻辑核心呢?
像我说的,默认的Python没有真正的多线程(因此不能真正使用逻辑核心),因为有全局解释器锁。你可以在程序的某些部分强制删除GIL,但如果你不知道自己在做什么,我认为最好不要这样做。
去除GIL绝对是很多研究的课题(请查看试验性的PyPy或Cython项目,它们都试图这样做)。
目前还没有真正的解决方案,因为它比看起来复杂得多。
我承认,还有另一种可以起作用的解决方案:
- 用C编写函数
- 使用ctype将其包装到Python中
- 使用Python多线程模块调用你封装的C函数
这将100%起作用,并且你将能够在Python中使用所有逻辑核心进行真正的多线程处理。 GIL不会打扰你,因为你不会执行真正的Python函数,而是执行C函数。
例如,一些库如NumPy可以在所有可用的线程上工作,因为它们是用C编写的。但如果您到达此点,我总是认为直接用C/C++编写程序可能是一个明智的考虑,因为这与原始的Python精神有很大区别。
**--> 别总是使用所有可用的物理内核 **
我经常看到人们这样做:“好的,我有8个物理内核,所以我将使用8个内核来处理我的工作。”它经常可以正常工作,但有时候会变得不太可取,特别是如果您的工作需要大量I/O。
对于高I/O需求的任务,尝试使用N-1个核心,并且你会发现在每个任务/平均值上,单个任务总是在N-1个内核上运行得更快。实际上,你的计算机执行许多不同的任务:USB、鼠标、键盘、网络、硬盘等等...即使在一个工作站上,周期性任务也会在后台执行,而你并不知道。如果你不让一个物理核心来管理这些任务,你的计算将会被定期中断(从内存中刷出/重新放回内存),这也会导致性能问题。
你可能会认为:“嗯,后台任务只会使用5%的CPU时间,所以还剩下95%”。但事实并非如此。
处理器一次只能处理一个任务。每次切换时,需要浪费相当大的时间将所有东西放回内存缓存/寄存器中的原位。然后,如果由于某种奇怪的原因,操作系统调度程序太频繁地进行此切换(这是无法控制的),所有这些计算时间都会永久丢失,你无能为力。
如果(有时会发生)由于某种未知原因,调度程序问题影响了30个任务的性能,而不仅仅是1个任务,这可能导致非常有趣的情况,即在29/30个物理核心上运行会比在30/30个物理核心上运行显着更快。
更多的CPU并不总是最好的
在使用multiprocessing.Pool时,很频繁地使用一个共享于进程之间的multiprocessing.Queue或管理器队列,以允许它们之间进行基本通信。但有时(我可能已经说过100次了,但我重申一下),在硬件相关的情况下,使用更多的CPU可能会在进程间通信/同步时创建瓶颈(但你应该为你特定的应用程序、代码实现和硬件进行基准测试)。在这些特定情况下,运行在较低的CPU数量上可能是有趣的,甚至尝试将同步任务委托给更快的处理器(当然,这里指的是在集群上运行的科学密集计算)。由于多进程通常被用于集群上,你必须注意到集群通常会因为节省能源而降低频率。因此,单核性能可能非常糟糕(通过更高数量的CPU来平衡),当你从本地计算机(少量核心,高单核性能)扩展你的代码到集群(大量核心,较低的单核性能)时,你的代码会根据单核性能/内核数量比率出现瓶颈,这使得有时非常恼人。
每个人都有使用尽可能多CPU的冲动。但是,针对这些情况进行基准测试是必需的。
典型的情况(例如在数据科学中)是有N个进程并行运行,并且你想要将结果汇总到一个文件中。因为你无法等待作业完成,所以通过一个特定的写入进程来完成它。写入程序将在他的multiprocessing.Queue中写入输出文件中的所有内容(单核且硬盘受限的过程)。N个进程填充multiprocessing.Queue。
想象一下,如果您有31个CPU向一个非常慢的CPU写入信息,那么您的性能将会降低(如果超过系统处理临时数据的能力,则可能会崩溃)。
--> 要点
- 使用psutil来计算逻辑/物理处理器,而不是multiprocessing.cpu_count()或任何其他方法
- 多进程只能在物理核心上工作(或者至少要进行基准测试来证明在您的情况下这不是真的)
- 多线程可以在逻辑核心上工作,但您需要用C编写并包装您的函数,或者删除全局锁定解释器(每次这样做时,世界某个地方就会有一只小猫惨遭毒手)
- 如果您正在尝试在纯Python代码上运行多线程,性能将会大幅下降,因此您99%的时间应该使用多进程
- 除非您的进程/线程有很长的暂停时间可以利用,否则永远不要使用超过可用数量的核心,并且如果您想尝试,请正确进行基准测试
- 如果您的任务需要进行I/O操作,应该让1个物理核心来处理I/O。如果您有足够的物理核心,这将是值得的。对于多进程实现,它需要使用N-1个物理核心。对于经典的双向多线程,这意味着要使用N-2个逻辑核心。
- 如果您需要更高的性能,请尝试PyPy(不适用于生产)或Cython,甚至可以用C编写代码
最后但并非最不重要的:如果您真的寻求性能,您应该绝对、始终、始终进行基准测试,而不是猜测任何事情。基准测试常常会揭示出奇怪的平台/硬件/驱动程序非常特定的行为,这些您根本不知道。
import psutils``psutil.cpu_count(logical=False)
似乎知道区别。 - user189035multiprocessing.Pool(4)
,并且没有其他进程运行,那么只有物理核心会被调用来执行计算? - user189035