835 Stimmen

Wie man die Anzahl der CPUs mit Python herausfindet

Ich möchte die Anzahl der CPUs auf dem lokalen Rechner unter Verwendung von Python herausfinden. Das Ergebnis sollte user/real sein, wie es von time(1) ausgegeben wird, wenn es mit einem optimal skalierenden Benutzerbereichs-Programm aufgerufen wird.

0 Stimmen

Wenn Sie Torch verwenden, können Sie import torch.multiprocessing; mp.cpu_count() ausführen.

7 Stimmen

Du solltest cpusets (in Linux) im Kopf behalten. Wenn du in einem cpuset bist, geben die unten stehenden Lösungen immer noch die Anzahl der echten CPUs im System an, nicht die Anzahl, die deinem Prozess zur Verfügung steht. /proc//status hat einige Zeilen, die dir die Anzahl der CPUs im aktuellen cpuset anzeigen: Suche nach Cpus_allowed_list.

1288voto

Nadia Alramli Punkte 105256

Wenn Sie Python mit einer Version >= 2.6 haben, können Sie einfach verwenden

import multiprocessing

multiprocessing.cpu_count()

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

8 Stimmen

Multiprocessing wird auch in 3.x unterstützt

3 Stimmen

Ich möchte hinzufügen, dass dies in IronPython nicht funktioniert, was eine NotImplementedError auslöst.

6 Stimmen

Dies gibt die Anzahl der CPUs auf verfügbar...nicht in Verwendung durch das Programm!

237voto

phihag Punkte 261131

Wenn Sie an der Anzahl der verfügbaren Prozessoren interessiert sind, müssen Sie zuerst cpuset überprüfen. Andernfalls (oder wenn cpuset nicht verwendet wird) ist multiprocessing.cpu_count() der richtige Weg in Python 2.6 und neuer. Die folgende Methode greift in älteren Versionen von Python auf ein paar alternative Methoden zurück:

import os
import re
import subprocess

def available_cpu_count():
    """Anzahl der verfügbaren virtuellen oder physischen CPUs auf diesem System, d.h.
    Benutzer/Real als Ausgabe von time(1), wenn sie mit einem optimal skalierenden
     Benutzerraumprogramm aufgerufen wird."""

    # cpuset
    # cpuset kann die Anzahl der *verfügbaren* Prozessoren beschränken
    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 in alten Versionen
    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

    # Andere UNIXes (heuristisch)
    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('Die Anzahl der CPUs auf diesem System kann nicht bestimmt werden')

0 Stimmen

Auf einem MacPro 1,0 unter dem neuesten Ubuntu, auf einem HP Laptop unter einem aktuellen Debian und auf einem alten eMachine unter einem alten Ubuntu sind die Ergebnisse von /proc/self/status jeweils ff, f und f, was entsprechend 8, 4 und 4 nach Ihrer (richtigen) Berechnung entspricht. Die tatsächlichen CPU-Nummern sind jedoch jeweils 4, 2 und 1. Ich finde, dass es möglicherweise besser ist, die Anzahl der Vorkommen des Wortes "processor" in /proc/cpuinfo zu zählen. (Oder habe ich die Frage falsch verstanden?)

1 Stimmen

Mit etwas weiterer Recherche --- wenn man das von "Googeln" sagen kann --- finde ich aus der Verwendung von /proc/cpuinfo, dass man für jede "Prozessor"-Auflistung das "Geschwister" mit den "CPU-Kernen" multipliziert, um die "Cpus_allowed"-Nummer zu erhalten. Und ich nehme an, dass die Geschwister auf Hyper-Threading verweisen, daher Ihr Verweis auf "virtuell". Aber die Tatsache bleibt, dass Ihre "Cpus_allowed"-Nummer auf meinem MacPro 8 beträgt, während Ihre multiprocessing.cpu_count()-Antwort 4 beträgt. Mein eigener open('/proc/cpuinfo').read().count('processor') ergibt auch 4, die Anzahl der physischen Kerne (zwei Dual-Core-Prozessoren).

2 Stimmen

open('/proc/self/status').read() vergisst, die Datei zu schließen. Verwenden Sie stattdessen with open('/proc/self/status') as f: f.read()

171voto

len(os.sched_getaffinity(0)) ist das, was Sie normalerweise wollen

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

os.sched_getaffinity(0) (in Python 3 hinzugefügt) gibt die Menge der verfügbaren CPUs zurück, unter Berücksichtigung des sched_setaffinity Linux-Systemaufrufs, der die CPUs einschränkt, auf denen ein Prozess und seine Kinder ausgeführt werden können.

0 bedeutet, den Wert für den aktuellen Prozess zu erhalten. Die Funktion gibt ein set() von erlaubten CPUs zurück, daher die Notwendigkeit von len().

multiprocessing.cpu_count() und os.cpu_count() hingegen geben nur die Gesamtzahl der logischen CPUs zurück, das heißt z.B. die Anzahl der CPUs unter Berücksichtigung von Hyperthreading.

Der Unterschied ist besonders wichtig, da bestimmte Cluster-Verwaltungssysteme wie Platform LSF die CPU-Auslastung von Jobs mit sched_getaffinity begrenzen.

Wenn Sie also multiprocessing.cpu_count() verwenden, könnte Ihr Skript versuchen, viel mehr Kerne zu verwenden, als verfügbar sind, was zu Überlastungen und Zeitüberschreitungen führen kann.

Wir können den Unterschied konkret sehen, indem wir die Affinität mit dem Dienstprogramm taskset einschränken, das uns ermöglicht, die Affinität eines Prozesses zu steuern.

Minimalbeispiel für taskset

Zum Beispiel, wenn ich Python auf nur einen Kern (Kern 0) in meinem 16-Kern-System beschränke:

taskset -c 0 ./main.py

mit dem Testskript:

main.py

#!/usr/bin/env python3

import multiprocessing
import os

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

dann ist die Ausgabe:

16
16
1

Vs nproc

nproc respektiert standardmäßig die Affinität und:

taskset -c 0 nproc

gibt aus:

1

und man nproc macht das sehr deutlich:

Gibt die Anzahl der verfügbaren Verarbeitungseinheiten aus

Also verhält sich len(os.sched_getaffinity(0)) standardmäßig wie nproc.

nproc hat die Option --all für den selteneren Fall, dass Sie die Anzahl der physischen CPUs erhalten möchten, ohne taskset zu berücksichtigen:

taskset -c 0 nproc --all

Dokumentation zu os.cpu_count

Die Dokumentation von os.cpu_count erwähnt dies auch kurz https://docs.python.org/3.8/library/os.html#os.cpu_count

Diese Zahl entspricht nicht der Anzahl der CPUs, die der aktuelle Prozess verwenden kann. Die Anzahl der verwendbaren CPUs kann mit len(os.sched_getaffinity(0)) erhalten werden

Derselbe Kommentar wird auch in der Dokumentation von multiprocessing.cpu_count kopiert: https://docs.python.org/3/library/multiprocessing.html#multiprocessing.cpu_count

Im Quellcode von 3.8 unter Lib/multiprocessing/context.py sehen wir auch, dass multiprocessing.cpu_count einfach an os.cpu_count weiterleitet, mit der Ausnahme, dass die multiprocessing-Version eine Ausnahme auslöst, anstatt None zurückzugeben, wenn os.cpu_count fehlschlägt:

    def cpu_count(self):
        '''Gibt die Anzahl der CPUs im System zurück'''
        num = os.cpu_count()
        if num is None:
            raise NotImplementedError('Anzahl der CPUs kann nicht ermittelt werden')
        else:
            return num

Verfügbarkeit von 3.8: Systeme mit einer nativen sched_getaffinity-Funktion

Der einzige Nachteil dieser os.sched_getaffinity ist, dass dies bis Python 3.8 offenbar nur UNIX-exklusiv ist.

cpython 3.8 versucht anscheinend einfach, während der Konfigurationszeit ein kleines C "Hello World" mit einem Aufruf der Funktion sched_setaffinity zu kompilieren, und wenn diese nicht vorhanden ist, wird HAVE_SCHED_SETAFFINITY nicht gesetzt sein und die Funktion wird wahrscheinlich fehlen:

psutil.Process().cpu_affinity(): Drittanbieterversion mit einer Windows-Portierung

Das Drittanbieter-Paket psutil package (pip install psutil) wurde in einem Beitrag erwähnt: https://stackoverflow.com/a/14840102/895245 aber nicht die Funktion cpu_affinity: https://psutil.readthedocs.io/en/latest/#psutil.Process.cpu_affinity

Verwendung:

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

Diese Funktion tut dasselbe wie die Standardbibliothek os.sched_getaffinity unter Linux, aber sie haben sie auch für Windows implementiert, indem sie einen Aufruf der Windows-API-Funktion GetProcessAffinityMask machen:

Mit anderen Worten: Die Windows-Benutzer müssen aufhören, faul zu sein und einen Patch an die Upstream-Standardbibliothek senden :-)

Getestet in Ubuntu 16.04, Python 3.5.2.

10 Stimmen

Nur verfügbar auf Unix.

2 Stimmen

Funktioniert auf Jupyter Notebook!

0 Stimmen

Nur zur Klarstellung, unterstützt dies auch CPU-Limits in Docker-Containern?

136voto

Bakuriu Punkte 91990

Eine weitere Option ist die Verwendung der psutil -Bibliothek, die sich in diesen Situationen immer als nützlich erweist:

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

Dies sollte auf allen von psutil unterstützten Plattformen funktionieren (Unix und Windows).

Beachten Sie, dass in einigen Fällen multiprocessing.cpu_count eine NotImplementedError auslösen kann, während psutil die Anzahl der CPUs ermitteln kann. Dies liegt einfach daran, dass psutil zuerst versucht, die gleichen Techniken wie multiprocessing zu verwenden und bei einem Fehlschlag auch andere Techniken verwendet.

17 Stimmen

Dieser ist wirklich gut, wenn man bedenkt, dass die verwendete Methode es ermöglicht festzustellen, ob die CPU-Kerne logische oder physische sind. psutil.cpu_count(logical = True)

0 Stimmen

Hallo @Bakuriu, Gibt es einen Weg, um die Anzahl der CPU-Kerne herauszufinden, die von einem bestimmten Prozess mit psutil verwendet werden?

5 Stimmen

@Devilhunter Auf Windows auf meinem Intel i7-8700 psutil.cpu_count() gibt 12 (es ist eine 6-Kern-CPU mit Hyperthreading). Dies liegt daran, dass das Standardargument von logical True ist, also müssen Sie explizit psutil.cpu_count(logical = False) eingeben, um die Anzahl der physischen Kerne zu erhalten.

72voto

jfs Punkte 370717

In Python 3.4+: os.cpu_count().

multiprocessing.cpu_count() wird in Bezug auf diese Funktion implementiert, wirft jedoch NotImplementedError auf, wenn os.cpu_count() None zurückgibt ("Anzahl der CPUs kann nicht bestimmt werden").

7 Stimmen

Siehe auch die Dokumentation von cpu_count. len(os.sched_getaffinity(0)) könnte je nach Zweck besser sein.

3 Stimmen

@Albert ja, die Anzahl der CPUs im System (os.cpu_count() - was der OP fragt) kann von der Anzahl der CPUs abweichen, die dem aktuellen Prozess zur Verfügung stehen (os.sched_getaffinity(0)).

0 Stimmen

Ich weiß. Ich wollte das nur hinzufügen, damit auch andere Leser, die diesen Unterschied vielleicht übersehen, ein vollständigeres Bild davon bekommen können.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X