web-dev-qa-db-fra.com

Suivi * maximum * d'utilisation de la mémoire par une fonction Python

Je veux savoir quelle est la quantité maximale de RAM allouée lors de l'appel d'une fonction (en Python). Il y a d'autres questions sur SO liées au suivi de l'utilisation de RAM:

Quel profileur de mémoire Python est recommandé?

Comment profiler l'utilisation de la mémoire en Python?

mais ceux-ci semblent vous permettre davantage de suivre l'utilisation de la mémoire au moment où la méthode heap() (dans le cas de guppy) est appelée. Cependant, ce que je veux suivre est une fonction dans une bibliothèque externe que je ne peux pas modifier et qui utilise de plus en plus de RAM, mais la libère une fois l'exécution de la fonction terminée. Existe-t-il un moyen de savoir quelle est la quantité totale de RAM utilisée lors de l'appel de fonction?

33
astrofrog

Cette question semblait plutôt intéressante et m'a donné une raison de regarder Guppy/Heapy, je vous en remercie. 

J'ai essayé pendant environ 2 heures de faire surveiller par Heapy un appel/un processus sans modifier sa source avec zéro chance. 

J'ai trouvé un moyen d'accomplir votre tâche en utilisant la bibliothèque Python intégrée resource . Notez que la documentation n'indique pas ce que la valeur RU_MAXRSS renvoie. Un autre SO utilisateur a noté qu'il était en Ko. Exécuter Mac OSX 7.3 et regarder mes ressources système grimper pendant le code de test ci-dessous, je pense que les valeurs renvoyées sont en octets, pas en kilo-octets. 

Une vue de 10 000 pieds sur la façon dont j'ai utilisé la bibliothèque resource pour surveiller l'appel de bibliothèque a été de lancer la fonction dans un thread séparé (pouvant être contrôlé) et de suivre les ressources système pour ce processus dans le thread principal. Ci-dessous, j'ai les deux fichiers que vous devez exécuter pour le tester.

Moniteur de ressources de bibliothèque - any_you_want.py

import resource
import time

from stoppable_thread import StoppableThread


class MyLibrarySniffingClass(StoppableThread):
    def __init__(self, target_lib_call, arg1, arg2):
        super(MyLibrarySniffingClass, self).__init__()
        self.target_function = target_lib_call
        self.arg1 = arg1
        self.arg2 = arg2
        self.results = None

    def startup(self):
        # Overload the startup function
        print "Calling the Target Library Function..."

    def cleanup(self):
        # Overload the cleanup function
        print "Library Call Complete"

    def mainloop(self):
        # Start the library Call
        self.results = self.target_function(self.arg1, self.arg2)

        # Kill the thread when complete
        self.stop()

def SomeLongRunningLibraryCall(arg1, arg2):
    max_dict_entries = 2500
    delay_per_entry = .005

    some_large_dictionary = {}
    dict_entry_count = 0

    while(1):
        time.sleep(delay_per_entry)
        dict_entry_count += 1
        some_large_dictionary[dict_entry_count]=range(10000)

        if len(some_large_dictionary) > max_dict_entries:
            break

    print arg1 + " " +  arg2
    return "Good Bye World"

if __== "__main__":
    # Lib Testing Code
    mythread = MyLibrarySniffingClass(SomeLongRunningLibraryCall, "Hello", "World")
    mythread.start()

    start_mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
    delta_mem = 0
    max_memory = 0
    memory_usage_refresh = .005 # Seconds

    while(1):
        time.sleep(memory_usage_refresh)
        delta_mem = (resource.getrusage(resource.RUSAGE_SELF).ru_maxrss) - start_mem
        if delta_mem > max_memory:
            max_memory = delta_mem

        # Uncomment this line to see the memory usuage during run-time 
        # print "Memory Usage During Call: %d MB" % (delta_mem / 1000000.0)

        # Check to see if the library call is complete
        if mythread.isShutdown():
            print mythread.results
            break;

    print "\nMAX Memory Usage in MB: " + str(round(max_memory / 1000.0, 3))

Stoppable Thread - stoppable_thread.py

import threading
import time

class StoppableThread(threading.Thread):
    def __init__(self):
        super(StoppableThread, self).__init__()
        self.daemon = True
        self.__monitor = threading.Event()
        self.__monitor.set()
        self.__has_shutdown = False

    def run(self):
        '''Overloads the threading.Thread.run'''
        # Call the User's Startup functions
        self.startup()

        # Loop until the thread is stopped
        while self.isRunning():
            self.mainloop()

        # Clean up
        self.cleanup()

        # Flag to the outside world that the thread has exited
        # AND that the cleanup is complete
        self.__has_shutdown = True

    def stop(self):
        self.__monitor.clear()

    def isRunning(self):
        return self.__monitor.isSet()

    def isShutdown(self):
        return self.__has_shutdown


    ###############################
    ### User Defined Functions ####
    ###############################

    def mainloop(self):
        '''
        Expected to be overwritten in a subclass!!
        Note that Stoppable while(1) is handled in the built in "run".
        '''
        pass

    def startup(self):
        '''Expected to be overwritten in a subclass!!'''
        pass

    def cleanup(self):
        '''Expected to be overwritten in a subclass!!'''
        pass
22
Adam Lewis

Il est possible de faire cela avec memory_profiler . La fonction memory_usage renvoie une liste de valeurs, qui représentent l'utilisation de la mémoire au fil du temps (par défaut sur des fragments de 0,1 seconde). Si vous avez besoin du maximum, prenez simplement le maximum de cette liste. Petit exemple:

from memory_profiler import memory_usage
from time import sleep

def f():
    # a function that with growing
    # memory consumption
    a = [0] * 1000
    sleep(.1)
    b = a * 100
    sleep(.1)
    c = b * 100
    return a

mem_usage = memory_usage(f)
print('Memory usage (in chunks of .1 seconds): %s' % mem_usage)
print('Maximum memory usage: %s' % max(mem_usage))

Dans mon cas (memory_profiler 0.25) si imprime la sortie suivante:

Memory usage (in chunks of .1 seconds): [45.65625, 45.734375, 46.41015625, 53.734375]
Maximum memory usage: 53.734375
15
Fabian Pedregosa

Cela semble fonctionner sous Windows. Je ne sais pas sur les autres systèmes d'exploitation.

In [50]: import os

In [51]: import psutil

In [52]: process = psutil.Process(os.getpid())

In [53]: process.get_ext_memory_info().peak_wset
Out[53]: 41934848
5
sethp

Vous pouvez utiliser une ressource de la bibliothèque python pour obtenir l’utilisation de la mémoire.

import resource
resource.getrusage(resource.RUSAGE_SELF).ru_maxrss

Il donnera l'utilisation de la mémoire en kilo-octets, pour convertir en diviser par 1000 Mo.

2
VPS

L'utilitaire Unix standard time suit l'utilisation maximale de la mémoire du processus ainsi que d'autres statistiques utiles pour votre programme.

Exemple de sortie (maxresident est l'utilisation maximale de la mémoire, en kilo-octets.):

> time python ./scalabilty_test.py
45.31user 1.86system 0:47.23elapsed 99%CPU (0avgtext+0avgdata 369824maxresident)k
0inputs+100208outputs (0major+99494minor)pagefaults 0swaps
0
Vader B