Cómo averiguar la cantidad de CPU que usan Python

537

Quiero saber la cantidad de CPU en la máquina local que usa Python. El resultado debe ser user/realcomo resultado time(1)cuando se llama con un programa de espacio de usuario de escala óptima.

phihag
fuente
3
Debe tener en cuenta cpusets (en Linux). Si está en un cpuset, las soluciones a continuación aún le darán la cantidad de CPU reales en el sistema, no la cantidad disponible para su proceso. /proc/<PID>/statustiene algunas líneas que le indican la cantidad de CPU en el cpuset actual: busque Cpus_allowed_list.
wpoely86

Respuestas:

854

Si tiene Python con una versión> = 2.6, simplemente puede usar

import multiprocessing

multiprocessing.cpu_count()

http://docs.python.org/library/multiprocessing.html#multiprocessing.cpu_count

Nadia Alramli
fuente
44
el multiprocesamiento también es compatible con 3.x
LittleByBlue
3
Quiero agregar que esto no funciona en IronPython, lo que genera un error NotImplemented.
Matthias
1
Esto proporciona la cantidad de CPU disponibles ... ¡no en uso por el programa!
amc
25
En Python 3.6.2 solo podía usaros.cpu_count()
Achilles
44
Además, como se indica a continuación, este recuento puede incluir cpus "virtual" con hipertrama, que puede no ser lo que desea si está programando tareas intensivas en CPU.
Christopher Barber
186

Si está interesado en la cantidad de procesadores disponibles para su proceso actual, primero debe verificar cpuset . De lo contrario (o si cpuset no está en uso), este multiprocessing.cpu_count()es el camino a seguir en Python 2.6 y versiones posteriores. El siguiente método recurre a un par de métodos alternativos en versiones anteriores de 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')
phihag
fuente
En un MacPro 1.0 con el último Ubuntu, en un portátil HP con un Debian reciente y en un eMachine antiguo con un Ubuntu antiguo, los resultados de cpus_allowed /proc/self/statusson respectivamente ff, f y f --- correspondientes a 8, 4 y 4 por tus (correctas) matemáticas. Sin embargo, el número real de CPU es, respectivamente, 4, 2 y 1. Me parece que contar el número de apariciones de la palabra "procesador" /proc/cpuinfopuede ser la mejor manera de hacerlo. (¿O tengo una pregunta incorrecta?)
Mike O'Connor
1
Con un poco más de investigación --- si eso se puede decir de "Google" --- descubro por el uso de /proc/cpuinfoeso que si para cualquiera de los listados de cada "procesador" se multiplican los "hermanos" por los "núcleos de la CPU" obtienes tu número "Cpus_allowed". Y deduzco que los hermanos se refieren al hiperhilo, de ahí su referencia a "virtual". Pero el hecho es que su número "Cpus_allowed" es 8 en mi MacPro, mientras que su multiprocessing.cpu_count()respuesta es 4. La mía open('/proc/cpuinfo').read().count('processor')también produce 4, el número de núcleos físicos (dos procesadores de doble núcleo).
Mike O'Connor
1
open('/proc/self/status').read()se olvida de cerrar el archivo. Usar en su with open('/proc/self/status') as f: f.read()lugar
timdiels
44
os.cpu_count()
goetzc
1
@amcgregor En este caso es aceptable, de acuerdo, solo se dejan abiertos los identificadores de archivo, lo que supongo que está bien si no está escribiendo un demonio / proceso de larga ejecución; que me temo podría terminar golpeando un máximo de archivos abiertos del sistema operativo. Es peor cuando se escribe en un archivo que necesita ser leído nuevamente antes de que finalice el proceso, pero ese no es el caso aquí, así que ese es un punto discutible. Sigue siendo una buena idea tener el hábito de usar withpara cuando encuentre un caso donde lo necesite.
Timdiels
91

Otra opción es usar la psutilbiblioteca, que siempre resulta útil en estas situaciones:

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

Esto debería funcionar en cualquier plataforma compatible con psutil(Unix y Windows).

Tenga en cuenta que en algunas ocasiones multiprocessing.cpu_countpuede aumentar un NotImplementedErrortiempo psutilpodrá obtener el número de CPU. Esto es simplemente porque psutilprimero intenta usar las mismas técnicas que usa multiprocessingy, si fallan, también usa otras técnicas.

Bakuriu
fuente
44
Este es realmente bueno, teniendo en cuenta que el método utilizado permite descubrir si los núcleos de la CPU son lógicos o físicos. psutil.cpu_count(logical = True)
Devilhunter
Hola @Bakuriu, ¿Hay alguna forma de obtener la cantidad de núcleos de CPU que utiliza un proceso específico usando psutil?
Saichand
1
@Devilhunter En Windows en mi Intel i7-8700 psutil.cpu_count()da 12 (es una CPU de 6 núcleos con hyperthreading). Esto se debe a que el argumento predeterminado de logicales True, por lo que debe escribir explícitamente psutil.cpu_count(logical = False)para obtener el número de núcleos físicos.
OscarVanL
52

En Python 3.4+: os.cpu_count () .

multiprocessing.cpu_count()se implementa en términos de esta función, pero aumenta NotImplementedErrorsi os.cpu_count()devuelve None("no se puede determinar el número de CPU").

jfs
fuente
44
Ver también la documentación de cpu_count. len(os.sched_getaffinity(0))podría ser mejor, dependiendo del propósito.
Albert
1
@ Albert sí, el número de CPU en el sistema (lo que os.cpu_count()pregunta OP) puede diferir del número de CPU que están disponibles para el proceso actual ( os.sched_getaffinity(0)).
jfs
Lo sé. Solo quería agregar eso para otros lectores, que podrían perderse esta diferencia, para obtener una imagen más completa de ellos.
Albert
1
Además: noos.sched_getaffinity(0) está disponible en BSD, por lo que se requiere el uso de (sin otra biblioteca externa, es decir). os.cpu_count()
Cometsong
1
Cabe señalar que os.sched_getaffinity no parece estar disponible en Windows.
manu3d
47

len(os.sched_getaffinity(0)) es lo que normalmente quieres

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

os.sched_getaffinity(0)(agregado en Python 3) devuelve el conjunto de CPU disponibles teniendo en cuenta la sched_setaffinityllamada al sistema Linux , que limita en qué CPU puede ejecutarse un proceso y sus hijos.

0significa obtener el valor para el proceso actual. La función devuelve una set()de las CPU permitidas, por lo tanto, la necesidad delen() .

multiprocessing.cpu_count() Por otro lado, solo devuelve el número total de CPU físicas.

La diferencia es especialmente importante porque ciertos sistemas de administración de clúster como Platform LSF limitan el uso de CPU de trabajo sched_getaffinity.

Por lo tanto, si lo usa multiprocessing.cpu_count(), su script podría intentar usar muchos más núcleos de los que tiene disponibles, lo que puede provocar sobrecarga y tiempos de espera.

Podemos ver la diferencia concretamente restringiendo la afinidad con la tasksetutilidad.

Por ejemplo, si restrinjo Python a solo 1 núcleo (núcleo 0) en mi sistema de 16 núcleos:

taskset -c 0 ./main.py

con el script de prueba:

main.py

#!/usr/bin/env python3

import multiprocessing
import os

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

entonces la salida es:

16
1

nproc sin embargo, respeta la afinidad por defecto y:

taskset -c 0 nproc

salidas:

1

y lo man nprochace bastante explícito:

imprime el número de unidades de procesamiento disponibles

nproctiene el --allindicador para el caso menos común en el que desea obtener el recuento físico de CPU:

taskset -c 0 nproc --all

El único inconveniente de este método es que parece ser solo UNIX. Supuse que Windows debe tener una API de afinidad similar, posiblemente SetProcessAffinityMask, así que me pregunto por qué no se ha portado. Pero no sé nada sobre Windows.

Probado en Ubuntu 16.04, Python 3.5.2.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
3
Solo disponible en Unix.
Christopher Barber
@ChristopherBarber gracias por la información, agregada a la respuesta.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
34

Si desea saber la cantidad de núcleos físicos (no núcleos virtuales hiperprocesados), aquí hay una solución independiente de la plataforma:

psutil.cpu_count(logical=False)

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

Tenga en cuenta que el valor predeterminado para logicales True, por lo tanto, si desea incluir núcleos hipertirados, puede usar:

psutil.cpu_count()

Esto dará el mismo número que os.cpu_count()y multiprocessing.cpu_count(), ninguno de los cuales tiene el logicalargumento de palabra clave.

Davoud Taghawi-Nejad
fuente
44
¿Cuál es la diferencia entre una CPU lógica y no una lógica? en mi laptop: psutil.cpu_count(logical=False) #4 psutil.cpu_count(logical=True) #8ymultiprocessing.cpu_count() #8
user305883
1
@ user305883 suponiendo que tiene una CPU x86, tiene hyperthreading en esta máquina, es decir, cada núcleo físico corresponde a dos hyperthreads (núcleos 'lógicos'). Hyperthreading permite que el núcleo físico se use para ejecutar instrucciones desde el subproceso B cuando partes del mismo están inactivas para el subproceso A (por ejemplo, esperando que se obtengan datos del caché o la memoria). Dependiendo de su código, uno puede obtener una o algunas decenas de porcentajes de utilización de núcleo adicional, pero está muy por debajo del rendimiento de un núcleo físico real.
Andre Holzner
23

Estos le dan el conteo de CPU hiperprocesado

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

Estos le dan el recuento de CPU de la máquina virtual

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

Solo importa si trabaja en máquinas virtuales.

yangliu2
fuente
Realmente no. Como se señaló, os.cpu_count()y multiprocessing.cpu_count()devolverá los recuentos de CPU hiperprocesados, no el recuento de CPU física real.
Christopher Barber
2
Si. Reescribí. Por lo general, es # de núcleos x 2. Lo que quiero decir es que si está en una máquina virtual, se obtuvieron 8 núcleos, pero su máquina host tiene 20 núcleos físicamente, el primer conjunto de comandos le da 20, el segundo conjunto de comandos le da 8.
yangliu2
21

multiprocessing.cpu_count()devolverá el número de CPU lógicas, por lo que si tiene una CPU de cuatro núcleos con hyperthreading, volverá 8. Si desea la cantidad de CPU físicas, use los enlaces de python para hwloc:

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

hwloc está diseñado para ser portátil en sistemas operativos y arquitecturas.

Douglas B. Grapa
fuente
En este caso, quiero el número de CPU lógicas (es decir, cuántos subprocesos debería iniciar si este programa escala realmente bien), pero la respuesta puede ser útil de todos modos.
phihag
77
opsutil.cpu_count(logical=False)
TimZaman el
8

No puedo entender cómo agregar al código o responder al mensaje, pero aquí hay soporte para jython que puede agregar antes de darse por vencido:

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

Esto puede funcionar para aquellos de nosotros que usamos diferentes sistemas operativos, pero queremos obtener lo mejor de todos los mundos:

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

También puede usar "joblib" para este propósito.

import joblib
print joblib.cpu_count()

Este método le dará la cantidad de cpus en el sistema. Sin embargo, debe instalarse JobLib. Puede encontrar más información sobre joblib aquí. https://pythonhosted.org/joblib/parallel.html

Alternativamente, puede usar el paquete numexpr de python. Tiene muchas funciones simples útiles para obtener información sobre la CPU del sistema.

import numexpr as ne
print ne.detect_number_of_cores()
amit12690
fuente
joblib usa el módulo de multiprocesamiento subyacente. Probablemente sea mejor llamar al multiprocesamiento directamente para esto.
ogrisel
1

Otra opción si no tienes Python 2.6:

import commands
n = commands.getoutput("grep -c processor /proc/cpuinfo")
Alkero
fuente
2
¡Gracias! Sin embargo, esto solo está disponible en Linux y ya está incluido en mi respuesta .
phihag