Pregunta Usar el 100% de todos los núcleos con el módulo de multiprocesamiento


Tengo dos códigos que estoy usando para aprender sobre el multiprocesamiento en Python 3.1. Mi objetivo es usar el 100% de todos los procesadores disponibles. Sin embargo, los fragmentos de código aquí solo alcanzan el 30% - 50% en todos los procesadores.

¿Hay alguna forma de 'forzar' a Python a usar todo el 100%? ¿El sistema operativo (Windows 7, 64 bits) limita el acceso de Python a los procesadores? Mientras se ejecutan los siguientes fragmentos de código, abro el administrador de tareas y miro el pico del procesador, pero nunca alcanzo y mantengo el 100%. Además de eso, puedo ver múltiples procesos python.exe creados y destruidos en el camino. ¿Cómo se relacionan estos procesos con los procesadores? Por ejemplo, si genero 4 procesos, cada proceso no usa su propio núcleo. En cambio, ¿qué están usando los procesos? ¿Comparten todos los núcleos? Y si es así, ¿es el sistema operativo el que obliga a los procesos a compartir los núcleos?

fragmento de código 1

import multiprocessing

def worker():
    #worker function
    print ('Worker')
    x = 0
    while x < 1000:
        print(x)
        x += 1
    return

if __name__ == '__main__':
    jobs = []
    for i in range(50):
        p = multiprocessing.Process(target=worker)
        jobs.append(p)
        p.start()

fragmento de código 2

from multiprocessing import Process, Lock

def f(l, i):
    l.acquire()
    print('worker ', i)
    x = 0
    while x < 1000:
        print(x)
        x += 1
    l.release()

if __name__ == '__main__': 
    lock = Lock()
    for num in range(50):
        Process(target=f, args=(lock, num)).start()

32
2018-04-25 23:30


origen


Respuestas:


Para usar el 100% de todos los núcleos, no cree ni destruya nuevos procesos.

Cree algunos procesos por núcleo y vincúlelos con una tubería.

En el nivel del sistema operativo, todos los procesos interconectados se ejecutan al mismo tiempo.

Mientras menos escriba (y cuanto más delegue en el sistema operativo), es más probable que use tantos recursos como sea posible.

python p1.py | python p2.py | python p3.py | python p4.py ...

Hará un uso máximo de su CPU.


31
2018-04-25 23:33



Puedes usar psutil a fijar cada proceso engendrado por multiprocessing a una CPU específica:

import multiprocessing as mp
import psutil


def spawn():
    procs = list()
    n_cpus = psutil.cpu_count()
    for cpu in range(n_cpus):
        affinity = [cpu]
        d = dict(affinity=affinity)
        p = mp.Process(target=run_child, kwargs=d)
        p.start()
        procs.append(p)
    for p in procs:
        p.join()
        print('joined')

def run_child(affinity):
    proc = psutil.Process()  # get self pid
    print('PID: {pid}'.format(pid=proc.pid))
    aff = proc.cpu_affinity()
    print('Affinity before: {aff}'.format(aff=aff))
    proc.cpu_affinity(affinity)
    aff = proc.cpu_affinity()
    print('Affinity after: {aff}'.format(aff=aff))


if __name__ == '__main__':
    spawn()

Nota: Como se comentó, psutil.Process.cpu_affinity no está disponible en macOS.


9
2018-02-12 20:23



Con respecto al fragmento de código 1: ¿Cuántos núcleos / procesadores tiene en su máquina de prueba? No le está haciendo ningún bien ejecutar 50 de estos procesos si solo tiene 2 núcleos de CPU. De hecho, está obligando al sistema operativo a pasar más tiempo cambiando de contexto para mover procesos dentro y fuera de la CPU que en el trabajo real.

Intente reducir el número de procesos generados a la cantidad de núcleos. Entonces, "para mí en el rango (50):" debería convertirse en algo así como:

import os;
# assuming you're on windows:
for i in range(int(os.environ["NUMBER_OF_PROCESSORS"])):
    ...

Con respecto al fragmento de código 2: está utilizando un multiprocesamiento. Bloqueo que solo puede mantenerse en un único proceso a la vez, por lo que limita completamente todo el paralelismo en esta versión del programa. Ha serializado cosas para que comience el proceso 1 al 50, un proceso aleatorio (digamos el proceso 7) adquiere el bloqueo. Los procesos 1-6 y 8-50 todos se sientan en la línea:

l.acquire()

Mientras están sentados allí, solo están esperando que se libere la cerradura. Dependiendo de la implementación de la primitiva de bloqueo, probablemente no estén usando ninguna CPU, simplemente están sentados allí utilizando recursos del sistema como la RAM, pero no están haciendo ningún trabajo útil con la CPU. El proceso 7 cuenta e imprime a 1000 y luego libera el bloqueo. El sistema operativo entonces es libre de programar aleatoriamente uno de los 49 procesos restantes para ejecutar. Cualquiera que se despierte primero adquirirá la cerradura siguiente y correrá mientras los 48 restantes esperan en la cerradura. Esto continuará para todo el programa.

Básicamente, el fragmento de código 2 es un ejemplo de lo que dificulta la concurrencia. Debe gestionar el acceso mediante muchos procesos o subprocesos a algún recurso compartido. En este caso particular, realmente no hay ninguna razón para que estos procesos necesiten esperar el uno del otro.

Así que de estos dos, el Snippet 1 está más cerca de una utilización más eficiente de la CPU. Creo que ajustar correctamente el número de procesos para que coincida con la cantidad de núcleos arrojará un resultado mucho mejor.


6
2018-04-26 04:05



Para responder tu pregunta):

¿Hay alguna forma de 'forzar' a Python a usar todo el 100%?

No es que haya oído hablar de

¿El sistema operativo (Windows 7, 64 bits) limita el acceso de Python a los procesadores?

Sí y No, sí: si python tomó el 100%, Windows se congelará. No, puede otorgar privilegios de administrador de Python que resultarán en un bloqueo.

¿Cómo se relacionan estos procesos con los procesadores?

No lo hacen, técnicamente en el nivel del sistema operativo, esos "procesos" python son subprocesos procesados ​​por el OS Handler ya que es necesario su manejo.

En cambio, ¿qué están usando los procesos? ¿Comparten todos los núcleos? Y si es así, ¿es el sistema operativo el que obliga a los procesos a compartir los núcleos?

Comparten todos los núcleos, a menos que inicie una única instancia de python que tenga una afinidad establecida en un núcleo determinado (en un sistema multinúcleo), sus procesos se dividirán en el procesamiento de que cada núcleo es libre. Así que sí, el sistema operativo está forzando el intercambio de núcleos por defecto (o Python es técnicamente)

Si está interesado en la afinidad central de Python, consulte paquete de afinidad para python.


-8
2018-04-25 23:43