如何使用Python查找CPU数量

807

我想使用Python知道本地机器上的CPU数量。当使用一个最佳缩放的仅用户空间程序调用time(1)时,结果应该输出user/real


7
在 Linux 中应该牢记 cpusets。如果你在一个 cpuset 中,下面的解决方案仍然会给出系统中真实 CPU 的数量,而不是可供您的进程使用的数量。/proc/<PID>/status 文件中有一些行告诉您当前 cpuset 中的 CPU 数量:查找 Cpus_allowed_list。 - wpoely86
如果您正在使用torch,您可以执行import torch.multiprocessing; mp.cpu_count() - Charlie Parker
15个回答

1248

6
多进程在 Python 3.x 中也得到了支持。 - LittleByBlue
3
我想强调的是,在IronPython中这个方法行不通,会引发NotImplementedError异常。 - Matthias
5
这将给出可用的CPU数量,而不是程序正在使用的数量! - amc
90
在Python 3.6.2中,我只能使用os.cpu_count()函数。 - Achilles
18
另外,正如下面所指出的,此计数可能包括“虚拟”的超线程 CPU,如果您正在安排 CPU 密集型任务,则可能不是您想要的。 - Christopher Barber
显示剩余7条评论

234

如果您想了解当前进程可以使用的处理器数量,首先需要查看cpuset,如果没有使用cpuset,或者您正在使用Python2.6或更新版本,则可以使用multiprocessing.cpu_count()方法。在旧版本的Python中,以下方法将回退到一些替代方法:

import os
import re
import subprocess


def available_cpu_count():
    """ Number of available virtual or physical CPUs on this system, i.e.
    user/real as output by time(1) when called with an optimally scaling
    userspace-only program"""

    # cpuset
    # cpuset may restrict the number of *available* processors
    try:
        m = re.search(r'(?m)^Cpus_allowed:\s*(.*)$',
                      open('/proc/self/status').read())
        if m:
            res = bin(int(m.group(1).replace(',', ''), 16)).count('1')
            if res > 0:
                return res
    except IOError:
        pass

    # Python 2.6+
    try:
        import multiprocessing
        return multiprocessing.cpu_count()
    except (ImportError, NotImplementedError):
        pass

    # https://github.com/giampaolo/psutil
    try:
        import psutil
        return psutil.cpu_count()   # psutil.NUM_CPUS on old versions
    except (ImportError, AttributeError):
        pass

    # POSIX
    try:
        res = int(os.sysconf('SC_NPROCESSORS_ONLN'))

        if res > 0:
            return res
    except (AttributeError, ValueError):
        pass

    # Windows
    try:
        res = int(os.environ['NUMBER_OF_PROCESSORS'])

        if res > 0:
            return res
    except (KeyError, ValueError):
        pass

    # jython
    try:
        from java.lang import Runtime
        runtime = Runtime.getRuntime()
        res = runtime.availableProcessors()
        if res > 0:
            return res
    except ImportError:
        pass

    # BSD
    try:
        sysctl = subprocess.Popen(['sysctl', '-n', 'hw.ncpu'],
                                  stdout=subprocess.PIPE)
        scStdout = sysctl.communicate()[0]
        res = int(scStdout)

        if res > 0:
            return res
    except (OSError, ValueError):
        pass

    # Linux
    try:
        res = open('/proc/cpuinfo').read().count('processor\t:')

        if res > 0:
            return res
    except IOError:
        pass

    # Solaris
    try:
        pseudoDevices = os.listdir('/devices/pseudo/')
        res = 0
        for pd in pseudoDevices:
            if re.match(r'^cpuid@[0-9]+$', pd):
                res += 1

        if res > 0:
            return res
    except OSError:
        pass

    # Other UNIXes (heuristic)
    try:
        try:
            dmesg = open('/var/run/dmesg.boot').read()
        except IOError:
            dmesgProcess = subprocess.Popen(['dmesg'], stdout=subprocess.PIPE)
            dmesg = dmesgProcess.communicate()[0]

        res = 0
        while '\ncpu' + str(res) + ':' in dmesg:
            res += 1

        if res > 0:
            return res
    except OSError:
        pass

    raise Exception('Can not determine number of CPUs on this system')

1
通过进一步的研究——如果可以这样说"谷歌"——我从使用/proc/cpuinfo中发现,对于每个"处理器"的列表中,如果将"兄弟姐妹"乘以"CPU内核",就可以得到"Cpus_allowed"数字。我了解到,兄弟姐妹是指超线程,因此你提到了"虚拟"。但事实仍然是,在我的MacPro上,你的"Cpus_allowed"数字是8,而你的multiprocessing.cpu_count()答案是4。我的open('/proc/cpuinfo').read().count('processor')也产生了4,即物理内核数(两个双核处理器)。 - Mike O'Connor
2
open('/proc/self/status').read()忘记关闭文件。请改用with open('/proc/self/status') as f: f.read() - Tim Diels
5
os.cpu_count() - goetz
@timdiels 当然是解释器实现的产物,但我没有看到问题中有关于使用哪个解释器的证据。在Pypy下,清理(调用__del__)将会在“最后一个引用被释放之后的未来某个时间点”发生,或者在解释器先关闭的情况下不会发生(https://pypy.readthedocs.io/en/latest/cpython_differences.html#differences-related-to-garbage-collection-strategies)。“或不”情况也是可以接受的;当进程关闭时,文件句柄无论如何都会关闭。 - amcgregor
1
在这种情况下,这是可以接受的,同意,只是文件句柄被保留打开状态,我想如果您不编写长时间运行的守护程序/进程,那么这是可以接受的;但是我担心可能会达到操作系统的最大打开文件句柄数。当需要在进程结束之前再次读取文件时,将数据写入文件时情况会更糟,但这里并非如此,因此这是一个无关紧要的问题。仍然有一个好习惯,即在遇到需要使用它的情况时使用“with”。 - Tim Diels
显示剩余7条评论

153

len(os.sched_getaffinity(0)) 是你通常想要的

https://docs.python.org/3/library/os.html#os.sched_getaffinity

os.sched_getaffinity(0)(在Python 3中添加)返回可用CPU集合,考虑到sched_setaffinityLinux系统调用,该调用限制了进程及其子进程可以运行的CPU。

0 表示获取当前进程的值。该函数返回 set() 允许使用的CPU,因此需要用到 len()

另一方面,multiprocessing.cpu_count()os.cpu_count() 只返回逻辑CPU的总数,即考虑超线程的 CPU 数量

这种差异尤其重要,因为某些集群管理系统(如Platform LSF)通过sched_getaffinity限制作业的CPU使用率。

因此,如果您使用multiprocessing.cpu_count(),您的脚本可能会尝试使用比可用核心更多的核心,这可能会导致超载和超时。
我们可以通过使用taskset实用程序限制亲和性来明确地看到差异,该实用程序允许我们控制进程的亲和性。
最小的taskset示例
例如,如果我在我的16核系统中将Python限制为只使用1个核心(核心0):
taskset -c 0 ./main.py

使用测试脚本:

main.py

#!/usr/bin/env python3

import multiprocessing
import os

print(multiprocessing.cpu_count())
print(os.cpu_count())
print(len(os.sched_getaffinity(0)))

那么输出结果为:

16
16
1

Vs nproc

nproc默认情况下会尊重亲和性并且:

taskset -c 0 nproc

输出:

1

man nproc非常明确:

打印可用处理单元的数量

因此,默认情况下,len(os.sched_getaffinity(0))的行为类似于nproc

nproc具有--all标志,用于较少见的情况,即您想在不考虑taskset的情况下获取物理CPU计数:

taskset -c 0 nproc --all

os.cpu_count 文档

os.cpu_count 的文档也简要提到了这一点 https://docs.python.org/3.8/library/os.html#os.cpu_count

这个数字并不等同于当前进程可以使用的 CPU 数量。可用 CPU 数量可以通过 len(os.sched_getaffinity(0)) 获得。

相同的注释也被复制到了 multiprocessing.cpu_count 的文档中:https://docs.python.org/3/library/multiprocessing.html#multiprocessing.cpu_count

从3.8源代码的Lib/multiprocessing/context.py下,我们还可以看到multiprocessing.cpu_count只是将其转发到os.cpu_count,除了如果os.cpu_count失败时,multiprocessing会抛出异常而不是返回None:

    def cpu_count(self):
        '''Returns the number of CPUs in the system'''
        num = os.cpu_count()
        if num is None:
            raise NotImplementedError('cannot determine number of cpus')
        else:
            return num

3.8 可用性:具有本地 sched_getaffinity 函数的系统

这个 os.sched_getaffinity 的唯一缺点是,截至 Python 3.8,它似乎只适用于 UNIX。

cpython 3.8 似乎只是在配置时尝试使用 sched_setaffinity 函数调用编译一个小的 C hello world,如果不存在,则不设置 HAVE_SCHED_SETAFFINITY 并且该函数可能会丢失:

psutil.Process().cpu_affinity():带有Windows端口的第三方版本

第三方psutil软件包pip install psutil)已经在https://dev59.com/_HNA5IYBdhLWcg3wVcJx#14840102中提到,但没有提到cpu_affinity函数:https://psutil.readthedocs.io/en/latest/#psutil.Process.cpu_affinity

用法:

import psutil
print(len(psutil.Process().cpu_affinity()))

这个函数在Linux上执行与标准库os.sched_getaffinity相同的功能,但他们还通过调用GetProcessAffinityMask Windows API函数来实现了Windows版本:

换句话说:那些Windows用户必须停止懒惰,向上游的stdlib发送补丁 :-)
在Ubuntu 16.04、Python 3.5.2中测试过。

9
仅适用于Unix操作系统。 - Christopher Barber
2
适用于Jupyter Notebook! - Paul
3
在 macOS 上:AttributeError: 'Process' 对象没有 'cpu_affinity' 属性 - Anentropic
2
@Anentropic 感谢您提供这些信息,真是太遗憾了。看来Mac用户也得研究一下 psutil 和上游的标准库了 :-) - Ciro Santilli OurBigBook.com
1
"multiprocessing.cpu_count() 和 os.cpu_count() 则仅返回物理 CPU 的总数。" <- 这两个函数都返回逻辑核心的数量:在超线程的情况下,这将是物理核心数量的两倍。 - Rich
显示剩余7条评论

135

另一个选择是使用psutil库,在这些情况下总是非常有用的:

>>> import psutil
>>> psutil.cpu_count()
2

这应该可以在任何 psutil 支持的平台上工作(Unix和Windows)。

请注意,在某些情况下,multiprocessing.cpu_count 可能会引发 NotImplementedError,而 psutil 将能够获取CPU数量。这只是因为 psutil 首先尝试使用与 multiprocessing 相同的技术,如果失败,则还使用其他技术。


16
这非常好,因为使用的方法可以确定CPU核心是逻辑核心还是物理核心。 psutil.cpu_count(logical=True) - Devilhunter
嗨@Bakuriu,有没有办法使用psutil获取特定进程正在使用的CPU核心数? - saichand
5
在我的英特尔i7-8700上,psutil.cpu_count()返回12(这是一款具有超线程技术的6核心CPU)。这是因为logical参数的默认值为True,所以你需要显式地写出psutil.cpu_count(logical = False)才能获取物理核心数。 - OscarVanL
2
我认为大多数用户想要的是psutil.Process().cpu_affinity(),如此解释在这里:https://dev59.com/_HNA5IYBdhLWcg3wVcJx#55423170 顺便说一句。 - Ciro Santilli OurBigBook.com
答案需要更新。 - The EasyLearn Academy
psutil.cpu_count(logical=False) - The EasyLearn Academy

67
在Python 3.4+中:os.cpu_count()可用于获取CPU数量。 multiprocessing.cpu_count()基于该函数实现,但如果os.cpu_count()返回None("无法确定CPU数量"),则会引发NotImplementedError异常。

7
请参阅 cpu_count 的文档。根据用途,len(os.sched_getaffinity(0)) 可能更好。 - Albert
2
@Albert 是的,系统中的 CPU 数量 (os.cpu_count() - OP 所询问的内容) 可能与当前进程可用的 CPU 数量 (os.sched_getaffinity(0)) 不同。 - jfs
我知道。我只是想为其他读者补充一下这个区别,让他们从中获得更完整的信息。 - Albert
1
另外,os.sched_getaffinity(0)在BSD上不可用,因此需要使用os.cpu_count()(不使用其他外部库)。 - Cometsong
1
需要注意的是,os.sched_getaffinity 在 Windows 上似乎不可用。 - manu3d

62

如果你想知道物理核心数(而不是虚拟超线程核心数),这里提供了一个平台无关的解决方案:

psutil.cpu_count(logical=False)

https://github.com/giampaolo/psutil/blob/master/INSTALL.rst

请注意,默认情况下 logical 的值为 True,所以如果您确实想包括超线程核心,则可以使用以下命令:

psutil.cpu_count()

这将提供与os.cpu_count()multiprocessing.cpu_count()相同的数字,它们都没有logical关键字参数。


8
逻辑 CPU 和非逻辑 CPU 有什么区别? 在我的笔记本电脑上: psutil.cpu_count(logical=False) 返回 4,表示物理 CPU 核心数。 psutil.cpu_count(logical=True) 返回 8,即逻辑 CPU 核心数。 multiprocessing.cpu_count() 返回 8,即逻辑 CPU 核心数。 - user305883
5
假设您拥有一台x86 CPU,那么这台机器上有超线程,也就是每个物理核对应两个超线程('逻辑'核心)。超线程允许在等待缓存或内存中获取数据时,使用物理核心来执行来自线程B的指令,从而提高核心利用率。根据您的代码,可以获得额外核心利用率的1%到几十个百分点,但远低于真正物理核心的性能。 - Andre Holzner
1
迄今为止最好的答案,但在众多其他答案中很难找到。 - Pavel Chernikov

32

这些代码可以获取超线程CPU数量:

  1. multiprocessing.cpu_count()
  2. os.cpu_count()

这些代码可以获取虚拟机CPU数量:

  1. psutil.cpu_count()
  2. numexpr.detect_number_of_cores()

仅适用于在虚拟机上工作的情况。


不完全是这样。如注释所述,os.cpu_count()multiprocessing.cpu_count() 返回的是超线程 CPU 数量,而非实际物理 CPU 数量。 - Christopher Barber
2
是的,我重新措辞了。通常是核心数 x 2。我的意思是,如果您在虚拟机上,该虚拟机分配了8个核心,但您的主机实际上有20个核心,则第一组命令会给您20,第二组命令会给您8。 - yangliu2

27

对于 Python 版本在 3.4 及以上,您可以使用:

import os
os.cpu_count()

如果你正在寻找Linux命令nproc的替代方案,你有以下选择

len(os.sched_getaffinity(0))

22

multiprocessing.cpu_count() 的返回值是逻辑 CPU 数量。所以,如果你有一个带有超线程的四核 CPU,它会返回 8。如果你想要物理 CPU 的数量,请使用 Python 绑定到 hwloc:

#!/usr/bin/env python
import hwloc
topology = hwloc.Topology()
topology.load()
print topology.get_nbobjs_by_type(hwloc.OBJ_CORE)

hwloc旨在跨操作系统和架构移植。


在这种情况下,我想要逻辑 CPU 的数量(即如果该程序能够很好地扩展,我应该启动多少个线程),但答案可能仍然有帮助。 - phihag
9
或者 psutil.cpu_count(logical=False) - TimZaman

13

这可能适用于那些使用不同操作系统/系统,但想要获得最佳效果的人:

import os
workers = os.cpu_count()
if 'sched_getaffinity' in dir(os):
    workers = len(os.sched_getaffinity(0))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接