Cómo averiguar la cantidad de CPU usando Python

Resuelto phihag asked hace 15 años • 15 respuestas

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

phihag avatar Jun 17 '09 17:06 phihag
Aceptado

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 avatar Jun 17 '2009 10:06 Nadia Alramli

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), 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 avatar Jun 17 '2009 10:06 phihag

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 considerando la sched_setaffinityllamada al sistema Linux , que limita en qué CPU se puede ejecutar un proceso y sus hijos.

0significa obtener el valor para el proceso actual. La función devuelve una set()cantidad de CPU permitidas, de ahí la necesidad de len().

multiprocessing.cpu_count()y os.cpu_count()por otro lado simplemente devuelve el número total de CPU lógicas, es decir, por ejemplo, el número de CPU considerando hyperthreading .

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

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

Podemos ver la diferencia concretamente restringiendo la afinidad con la tasksetutilidad, lo que nos permite controlar la afinidad de un proceso.

tasksetEjemplo mínimo

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:

principal.py

#!/usr/bin/env python3

import multiprocessing
import os

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

entonces la salida es:

16
16
1

contranproc

nprocrespeta la afinidad por defecto y:

taskset -c 0 nproc

salidas:

1

y man nproclo hace bastante explícito:

imprimir el número de unidades de procesamiento disponibles

Por tanto, len(os.sched_getaffinity(0))se comporta como nprocpor defecto.

nproctiene la --allbandera para el caso menos común en el que desea obtener el recuento de CPU física sin considerar el conjunto de tareas:

taskset -c 0 nproc --all

os.cpu_countdocumentación

La documentación de os.cpu_counttambién menciona brevemente esto https://docs.python.org/3.8/library/os.html#os.cpu_count

Este número no es equivalente al número de CPU que puede utilizar el proceso actual. El número de CPU utilizables se puede obtener conlen(os.sched_getaffinity(0))

El mismo comentario también se copia en la documentación de multiprocessing.cpu_count: https://docs.python.org/3/library/multiprocessing.html#multiprocessing.cpu_count

Desde la fuente 3.8 Lib/multiprocessing/context.pytambién vemos que multiprocessing.cpu_countsolo se reenvía a os.cpu_count, excepto que multiprocessinguno arroja una excepción en lugar de devolver Ninguno si os.cpu_countfalla:

    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 disponibilidad: sistemas con sched_getaffinityfunción nativa

El único inconveniente de esto os.sched_getaffinityes que parece ser UNIX solo a partir de Python 3.8.

cpython 3.8 parece simplemente intentar compilar un pequeño C hola mundo con una sched_setaffinityllamada de función durante el tiempo de configuración, y si no está presente, HAVE_SCHED_SETAFFINITYno está configurado y es probable que falte la función:

  • https://github.com/python/cpython/blob/v3.8.5/configure#L11523
  • https://github.com/python/cpython/blob/v3.8.5/Modules/posixmodule.c#L6457

psutil.Process().cpu_affinity(): versión de terceros con un puerto de Windows

psutilEl paquete de terceros ( pip install psutil) se mencionó en: https://stackoverflow.com/a/14840102/895245 pero no la cpu_affinityfunción: https://psutil.readthedocs.io/en/latest/#psutil.Process.cpu_affinity

Uso:

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

Esta función hace lo mismo que la biblioteca estándar os.sched_getaffinityen Linux, pero también la han implementado para Windows haciendo una llamada a la GetProcessAffinityMaskfunción API de Windows:

  • https://github.com/giampaolo/psutil/blob/ee60bad610822a7f630c52922b4918e684ba7695/psutil/_psutil_windows.c#L1112
  • https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getprocessaffinitymask

En otras palabras: esos usuarios de Windows tienen que dejar de ser vagos y enviar un parche a la stdlib ascendente :-)

Probado en Ubuntu 16.04, Python 3.5.2.

Otra opción es utilizar 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 psutily podrá obtener la cantidad de CPU. Esto se debe simplemente a que psutilprimero intenta utilizar las mismas técnicas que utiliza multiprocessingy, si fallan, también utiliza otras técnicas.

Bakuriu avatar Feb 12 '2013 19:02 Bakuriu

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()regresa None("no se puede determinar el número de CPU").

jfs avatar Sep 03 '2014 04:09 jfs