Como descobrir o número de CPUs usando python

537

Quero saber o número de CPUs na máquina local usando Python. O resultado deve ser a user/realsaída time(1)quando chamado com um programa somente para espaço do usuário da escala ideal.

phihag
fonte
3
Você deve ter cpusets (no Linux) em mente. Se você estiver em um cpuset, as soluções abaixo ainda fornecerão o número de CPUs reais no sistema, não o número disponível para o seu processo. /proc/<PID>/statuspossui algumas linhas que informam o número de CPUs no cpuset atual: procure Cpus_allowed_list.
precisa saber é o seguinte

Respostas:

854

Se você tem python com uma versão> = 2.6, pode simplesmente usar

import multiprocessing

multiprocessing.cpu_count()

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

Nadia Alramli
fonte
4
multiprocessamento também é suportado em 3.x
LittleByBlue
3
Quero acrescentar que isso não funciona no IronPython, o que gera um NotImplementedError.
Matthias
1
Isso fornece o número de CPUs disponíveis ... não em uso pelo programa!
amc
25
No Python 3.6.2 eu só podia usaros.cpu_count()
Achilles
4
Além disso, conforme observado abaixo, essa contagem pode incluir cpus com hyperthread "virtual", que podem não ser o que você deseja se estiver agendando tarefas com uso intensivo de cpu.
Christopher Barber
186

Se você estiver interessado no número de processadores disponíveis para o seu processo atual, verifique primeiro o cpuset . Caso contrário (ou se o cpuset não estiver em uso), multiprocessing.cpu_count()é o caminho a seguir no Python 2.6 e mais recente. O método a seguir recai em alguns métodos alternativos nas versões mais antigas do 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
fonte
Em um MacPro 1,0 executando o Ubuntu mais recente, em um laptop HP executando um Debian recente e em um eMachine antigo com um Ubuntu antigo, os resultados cpus_allowed de /proc/self/statussão respectivamente ff, ef ef --- correspondentes a 8, 4 e 4 pela sua matemática (correta). No entanto, os números reais de CPUs são respectivamente 4, 2 e 1. Acho que contar o número de ocorrências da palavra "processador" /proc/cpuinfopode ser o melhor caminho a percorrer. (Ou eu tenho o errado pergunta?)
Mike O'Connor
1
Com algumas pesquisas adicionais - se isso pode ser dito sobre "pesquisar no Google" -, acho que, pelo uso /proc/cpuinfodisso, se para qualquer uma das listagens de cada "processador" você multiplica os "irmãos" pelos "núcleos da CPU" você obtém seu número "Cpus_allowed". E entendo que os irmãos se referem à hiper-segmentação, daí a sua referência a "virtual". Mas o fato é que seu número "permitido por Cpus" é 8 no meu MacPro, enquanto sua multiprocessing.cpu_count()resposta é 4. A minha open('/proc/cpuinfo').read().count('processor')também produz 4, o número de núcleos físicos (dois processadores de núcleo duplo).
9788 Mike O'Connor
1
open('/proc/self/status').read()esquece de fechar o arquivo. Use em with open('/proc/self/status') as f: f.read()vez disso
timdiels 23/03
4
os.cpu_count()
22717 Goetzc
1
@amcgregor Neste caso, é aceitável, concordado, apenas manipular arquivos sendo deixados em aberto, o que eu acho que está bem se você não estiver escrevendo um daemon / processo de longa execução; que eu temo pode acabar atingindo um máximo de identificadores de arquivos abertos do sistema operacional. É pior ao gravar em um arquivo que precisa ser lido novamente antes do término do processo, mas esse não é o caso aqui, portanto é um ponto discutível. Ainda é uma boa idéia ter o hábito de usá with-lo quando encontrar um caso em que você precise.
timdiels
91

Outra opção é usar a psutilbiblioteca, que sempre é útil nessas situações:

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

Isso deve funcionar em qualquer plataforma suportada por psutil(Unix e Windows).

Observe que, em algumas ocasiões, multiprocessing.cpu_countpode demorar um NotImplementedErrorpouco psutilpara conseguir o número de CPUs. Isso ocorre simplesmente porque psutilprimeiro tenta usar as mesmas técnicas usadas multiprocessinge, se elas falharem, também usa outras técnicas.

Bakuriu
fonte
4
Esse é realmente bom, considerando que o método usado permite descobrir se os núcleos da CPU são lógicos ou físicos. psutil.cpu_count(logical = True)
Devilhunter #
Oi @Bakuriu, Existe alguma maneira de obter o número de núcleos de CPU sendo usados ​​por um processo específico usando o psutil?
saichand
1
@Devilhunter No Windows, no meu Intel i7-8700, psutil.cpu_count()obtém 12 (é uma CPU de 6 núcleos com hyperthreading). Isso ocorre porque o argumento padrão de logicalé True, então você precisa escrever explicitamente psutil.cpu_count(logical = False)para obter o número de núcleos físicos.
OscarVanL 24/04
52

No Python 3.4 ou superior : os.cpu_count () .

multiprocessing.cpu_count()é implementado em termos dessa função, mas aumenta NotImplementedErrorse os.cpu_count()retorna None("não é possível determinar o número de CPUs").

jfs
fonte
4
Veja também a documentação de cpu_count. len(os.sched_getaffinity(0))pode ser melhor, dependendo da finalidade.
Albert
1
@ Albert Sim, o número de CPUs no sistema (o que o os.cpu_count()OP solicita) pode diferir do número de CPUs disponíveis para o processo atual ( os.sched_getaffinity(0)).
JFS
Eu sei. Eu só queria acrescentar isso para outros leitores, que podem perder essa diferença, para obter uma imagem mais completa deles.
Albert
1
Além disso: o nãoos.sched_getaffinity(0) está disponível no BSD, portanto é necessário o uso de (sem outra biblioteca externa). os.cpu_count()
Cometsong 14/05/19
1
Note-se que os.sched_getaffinity não parece estar disponível no Windows.
manu3d
47

len(os.sched_getaffinity(0)) é o que você geralmente quer

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

os.sched_getaffinity(0)(adicionado em Python 3) retorna o conjunto de CPUs disponíveis, considerando a sched_setaffinitychamada do sistema Linux , que limita em quais CPUs um processo e seus filhos podem executar.

0significa obter o valor para o processo atual. A função retorna uma set()das CPUs permitidas, portanto, a necessidade len().

multiprocessing.cpu_count() por outro lado, apenas retorna o número total de CPUs físicas.

A diferença é especialmente importante porque certos sistemas de gerenciamento de cluster, como o Platform LSF, limitam o uso da CPU do trabalho sched_getaffinity.

Portanto, se você usar multiprocessing.cpu_count(), seu script pode tentar usar muito mais núcleos do que os disponíveis, o que pode levar a sobrecarga e tempo limite.

Podemos ver a diferença concretamente restringindo a afinidade com a tasksetutilidade.

Por exemplo, se eu restringir o Python a apenas 1 núcleo (núcleo 0) no meu sistema de 16 núcleos:

taskset -c 0 ./main.py

com o script de teste:

main.py

#!/usr/bin/env python3

import multiprocessing
import os

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

então a saída é:

16
1

nproc no entanto, respeita a afinidade por padrão e:

taskset -c 0 nproc

saídas:

1

e man nprocdeixa isso bem explícito:

imprimir o número de unidades de processamento disponíveis

nprocpossui o --allsinalizador para o caso menos comum em que você deseja obter a contagem física de CPU:

taskset -c 0 nproc --all

A única desvantagem desse método é que ele parece ser apenas UNIX. Suponho que o Windows deve ter uma API de afinidade semelhante, possivelmente SetProcessAffinityMask, então me pergunto por que não foi portada. Mas não sei nada sobre o Windows.

Testado no Ubuntu 16.04, Python 3.5.2.

Ciro Santilli adicionou uma nova foto
fonte
3
Disponível apenas no Unix.
Christopher Barber
@ChristopherBarber obrigado pela informação, adicionada à resposta.
Ciro Santilli publicou
34

Se você deseja saber o número de núcleos físicos (não núcleos virtuais com hyperthread), aqui está uma solução independente de plataforma:

psutil.cpu_count(logical=False)

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

Observe que o valor padrão para logicalé True, portanto, se você deseja incluir núcleos com hyperthread, pode usar:

psutil.cpu_count()

Isso fornecerá o mesmo número que os.cpu_count()e multiprocessing.cpu_count(), nenhum dos quais possui o logicalargumento de palavra - chave.

Davoud Taghawi-Nejad
fonte
4
Qual é a diferença entre uma CPU lógica e não não lógica? no meu laptop: psutil.cpu_count(logical=False) #4 psutil.cpu_count(logical=True) #8emultiprocessing.cpu_count() #8
user305883 14/10
1
@ user305883 Supondo que você tenha uma CPU x86, você tem um hyperthreading nesta máquina, ou seja, cada núcleo físico corresponde a dois hyperthreads (núcleos 'lógicos'). O Hyperthreading permite que o núcleo físico seja usado para executar instruções do encadeamento B quando partes dele estão ociosas para o encadeamento A (por exemplo, aguardando a busca de dados do cache ou da memória). Dependendo do seu código, é possível obter uma ou algumas dezenas de porcentagens de utilização adicional do núcleo, mas está muito abaixo do desempenho de um núcleo físico real.
Andre Holzner
23

Isso fornece a contagem de CPU com hyperthread

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

Isso fornece a contagem de CPU da máquina virtual

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

Só importa se você trabalha em VMs.

yangliu2
fonte
Na verdade não. Conforme observado, os.cpu_count()e multiprocessing.cpu_count()retornará as contagens de CPU com hyperthread, e não a contagem física real da CPU.
Christopher Barber
2
Sim. Eu reformulei. Normalmente, é o número de núcleos x 2. O que quero dizer é que, se você estiver em uma máquina virtual, possui 8 núcleos, mas sua máquina host possui 20 núcleos fisicamente, o primeiro conjunto de comandos fornece 20, o segundo conjunto de comandos fornece 8.
yangliu2
21

multiprocessing.cpu_count()retornará o número de CPUs lógicas; portanto, se você tiver uma CPU quad-core com hyperthreading, ela retornará 8. Se você deseja o número de CPUs físicas, use as ligações python para hwloc:

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

O hwloc foi projetado para ser portátil em sistemas operacionais e arquiteturas.

Douglas B. Staple
fonte
Nesse caso, quero o número de CPUs lógicas (ou seja, quantos threads devo iniciar se esse programa for muito bom), mas a resposta pode ser útil, no entanto.
phihag
7
oupsutil.cpu_count(logical=False)
TimZaman 30/11/16
8

Não é possível descobrir como adicionar ao código ou responder à mensagem, mas aqui está o suporte ao jython que você pode aderir antes de desistir:

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

Isso pode funcionar para aqueles que usam sistemas operacionais diferentes, mas querem obter o melhor de todos os mundos:

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

Você também pode usar "joblib" para esse fim.

import joblib
print joblib.cpu_count()

Este método fornece o número de cpus no sistema. O joblib precisa ser instalado. Mais informações sobre joblib podem ser encontradas aqui https://pythonhosted.org/joblib/parallel.html

Como alternativa, você pode usar o pacote numexpr de python. Possui muitas funções simples úteis para obter informações sobre a CPU do sistema.

import numexpr as ne
print ne.detect_number_of_cores()
amit12690
fonte
joblib usa o módulo de multiprocessamento subjacente. Provavelmente, é melhor chamar o multiprocessamento diretamente para isso.
ogrisel
1

Outra opção se você não possui o Python 2.6:

import commands
n = commands.getoutput("grep -c processor /proc/cpuinfo")
Alkero
fonte
2
Obrigado! Porém, isso está disponível apenas no Linux e já está incluído na minha resposta .
Phihag 29/08