Saltar al contenido

Limite el uso total de la CPU en el multiprocesamiento de Python

Hola, hemos encontrado la solución a tu pregunta, deslízate y la verás aquí.

Solución:

La solución depende de lo que quieras hacer. Aquí hay algunas opciones:

Prioridades más bajas de procesos

Usted puede nice los subprocesos. De esta manera, aunque seguirán consumiendo el 100% de la CPU, cuando inicie otras aplicaciones, el sistema operativo dará preferencia a las otras aplicaciones. Si desea dejar un cálculo intensivo en trabajo ejecutándose en el fondo de su computadora portátil y no le importa que el ventilador de la CPU esté funcionando todo el tiempo, establezca el valor agradable con psutils es tu solución. Este script es un script de prueba que se ejecuta en todos los núcleos durante el tiempo suficiente para que pueda ver cómo se comporta.

from multiprocessing import Pool, cpu_count
import math
import psutil
import os

def f(i):
    return math.sqrt(i)

def limit_cpu():
    "is called at every process start"
    p = psutil.Process(os.getpid())
    # set to lowest priority, this is windows only, on Unix use ps.nice(19)
    p.nice(psutil.BELOW_NORMAL_PRIORITY_CLASS)

if __name__ == '__main__':
    # start "number of cores" processes
    pool = Pool(None, limit_cpu)
    for p in pool.imap(f, range(10**8)):
        pass

El truco es que limit_cpu se ejecuta al comienzo de cada proceso (ver initializer argumento en el doc). Mientras que Unix tiene niveles de -19 (prio más alto) a 19 (prio más bajo), Windows tiene algunos niveles distintos para dar prioridad. BELOW_NORMAL_PRIORITY_CLASS probablemente se adapte mejor a sus necesidades, también hay IDLE_PRIORITY_CLASS que dice que Windows ejecutará su proceso solo cuando el sistema esté inactivo.

Puede ver la prioridad si cambia al modo de detalle en el Administrador de tareas y hace clic con el botón derecho en el proceso:

ingrese la descripción de la imagen aquí

Menor número de procesos

Aunque ha rechazado esta opción, aún podría ser una buena opción: digamos que limita la cantidad de subprocesos a la mitad de los núcleos de la CPU usando pool = Pool(max(cpu_count()//2, 1)) luego, el sistema operativo inicialmente ejecuta esos procesos en la mitad de los núcleos de la CPU, mientras que los demás permanecen inactivos o simplemente ejecutan las otras aplicaciones que se están ejecutando actualmente. Después de un breve período de tiempo, el sistema operativo reprograma los procesos y podría moverlos a otros núcleos de CPU, etc. Tanto los sistemas basados ​​en Windows como Unix se comportan de esta manera.

Windows: Ejecución de 2 procesos en 4 núcleos:

OSX: ejecución de 4 procesos en 8 núcleos:

ingrese la descripción de la imagen aquí

Ves que ambos sistemas operativos equilibran el proceso entre los núcleos, aunque no de manera uniforme, por lo que todavía ves algunos núcleos con porcentajes más altos que otros.

Dormir

Si desea estar absolutamente seguro de que sus procesos nunca comen el 100% de un núcleo determinado (por ejemplo, si desea evitar que el ventilador de la CPU suba), puede ejecutar el modo de suspensión en su función de procesamiento:

from time import sleep

def f(i):
    sleep(0.01)
    return math.sqrt(i)

Esto hace que el sistema operativo “programe” su proceso para 0.01 segundos para cada cálculo y deja espacio para otras aplicaciones. Si no hay otras aplicaciones, entonces el núcleo de la CPU está inactivo, por lo que nunca llegará al 100%. Tendrá que jugar con diferentes duraciones de suspensión, también variará de una computadora a otra en la que lo ejecute. Si quieres que sea muy sofisticado, puedes adaptar el sueño en función de lo que cpu_times() informes.

En el nivel del sistema operativo

puedes usar nice para establecer una prioridad para un solo comando. También puede iniciar un script de Python con nice. (A continuación, de: http://blog.scoutapp.com/articles/2014/11/04/restricting-process-cpu-usage-using-nice-cpulimit-and-cgroups)

bonito

El comando nice modifica el nivel de prioridad de un proceso para que se ejecute con menos frecuencia. Esto es útil cuando necesita ejecutar una tarea de uso intensivo de la CPU como un trabajo en segundo plano o por lotes. El nivel de amabilidad varía de -20 (programación más favorable) a 19 (menos favorable). Los procesos en Linux se inician con un valor predeterminado de 0. El comando nice (sin ningún parámetro adicional) iniciará un proceso con un niceness de 10. En ese nivel, el programador lo verá como una tarea de menor prioridad y le dará menos recursos de CPU. Inicie dos tareas matho-primes, una con nice y uno sin:

nice matho-primes 0 9999999999 > /dev/null &matho-primes 0 9999999999 > /dev/null &
matho-primes 0 9999999999 > /dev/null &

Ahora corre arriba.

ingrese la descripción de la imagen aquí

Como función en Python

Otro enfoque es usar psutils para verificar el promedio de carga de la CPU durante el último minuto y luego hacer que sus subprocesos verifiquen el promedio de carga de la CPU y pongan en cola otro subproceso si está por debajo del objetivo de carga de CPU especificado, y suspender o eliminar el subproceso si está por encima del objetivo de carga de la CPU. Esto saldrá de su camino cuando esté usando su computadora, pero mantendrá una carga constante de CPU.

# Import Python modules
import time
import os
import multiprocessing
import psutil
import math
from random import randint

# Main task function
def main_process(item_queue, args_array):
    # Go through each link in the array passed in.
    while not item_queue.empty():
        # Get the next item in the queue
        item = item_queue.get()
        # Create a random number to simulate threads that
        # are not all going to be the same
        randomizer = randint(100, 100000)
        for i in range(randomizer):
            algo_seed = math.sqrt(math.sqrt(i * randomizer) % randomizer)
        # Check if the thread should continue based on current load balance
        if spool_down_load_balance():
            print "Process " + str(os.getpid()) + " saying goodnight..."
            break

# This function will build a queue and
def start_thread_process(queue_pile, args_array):
    # Create a Queue to hold link pile and share between threads
    item_queue = multiprocessing.Queue()
    # Put all the initial items into the queue
    for item in queue_pile:
        item_queue.put(item)
    # Append the load balancer thread to the loop
    load_balance_process = multiprocessing.Process(target=spool_up_load_balance, args=(item_queue, args_array))
    # Loop through and start all processes
    load_balance_process.start()
    # This .join() function prevents the script from progressing further.
    load_balance_process.join()

# Spool down the thread balance when load is too high
def spool_down_load_balance():
    # Get the count of CPU cores
    core_count = psutil.cpu_count()
    # Calulate the short term load average of past minute
    one_minute_load_average = os.getloadavg()[0] / core_count
    # If load balance above the max return True to kill the process
    if one_minute_load_average > args_array['cpu_target']:
        print "-Unacceptable load balance detected. Killing process " + str(os.getpid()) + "..."
        return True

# Load balancer thread function
def spool_up_load_balance(item_queue, args_array):

    print "[Starting load balancer...]"
    # Get the count of CPU cores
    core_count = psutil.cpu_count()
    # While there is still links in queue
    while not item_queue.empty():
        print "[Calculating load balance...]"
        # Check the 1 minute average CPU load balance
        # returns 1,5,15 minute load averages
        one_minute_load_average = os.getloadavg()[0] / core_count
        # If the load average much less than target, start a group of new threads
        if one_minute_load_average < args_array['cpu_target'] / 2:
            # Print message and log that load balancer is starting another thread
            print "Starting another thread group due to low CPU load balance of: " + str(one_minute_load_average * 100) + "%"
            time.sleep(5)
            # Start another group of threads
            for i in range(3):
                start_new_thread = multiprocessing.Process(target=main_process,args=(item_queue, args_array))
                start_new_thread.start()
            # Allow the added threads to have an impact on the CPU balance
            # before checking the one minute average again
            time.sleep(20)

        # If load average less than target start single thread
        elif one_minute_load_average < args_array['cpu_target']:
            # Print message and log that load balancer is starting another thread
            print "Starting another single thread due to low CPU load balance of: " + str(one_minute_load_average * 100) + "%"
            # Start another thread
            start_new_thread = multiprocessing.Process(target=main_process,args=(item_queue, args_array))
            start_new_thread.start()
            # Allow the added threads to have an impact on the CPU balance
            # before checking the one minute average again
            time.sleep(20)

        else:
            # Print CPU load balance
            print "Reporting stable CPU load balance: " + str(one_minute_load_average * 100) + "%"
            # Sleep for another minute while
            time.sleep(20)

if __name__=="__main__":

    # Set the queue size
    queue_size = 10000
    # Define an arguments array to pass around all the values
    args_array = 
        # Set some initial CPU load values as a CPU usage goal
        "cpu_target" : 0.60,
        # When CPU load is significantly low, start this number
        # of threads
        "thread_group_size" : 3
    

    # Create an array of fixed length to act as queue
    queue_pile = list(range(queue_size))
    # Set main process start time
    start_time = time.time()
    # Start the main process
    start_thread_process(queue_pile, args_array)
    print '[Finished processing the entire queue! Time consuming:0 Time Finished: 1]'.format(time.time() - start_time, time.strftime("%c"))

Aquí puedes ver las comentarios y valoraciones de los usuarios

Te invitamos a añadir valor a nuestro contenido informacional participando con tu veteranía en las anotaciones.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *