Отслеживание * максимального * использования памяти функцией Python
Я хочу узнать, каков максимальный объем оперативной памяти, выделяемой во время вызова функции (в Python). Есть и другие вопросы по SO, связанные с отслеживанием использования оперативной памяти:
Какой профилировщик памяти Python рекомендуется?
Как мне профилировать использование памяти в Python?
но они, кажется, позволяют вам больше отслеживать использование памяти во время heap()
метод (в случае гуппи) вызывается. Тем не менее, я хочу отследить функцию во внешней библиотеке, которую я не могу изменить, и которая увеличивает объем используемой оперативной памяти, но затем освобождает ее после завершения выполнения функции. Есть ли способ узнать, какой общий объем оперативной памяти использовался во время вызова функции?
6 ответов
Этот вопрос показался мне довольно интересным, и он дал мне повод заглянуть в Гуппи / Хипи, за что я вам благодарен.
Я около 2 часов пытался заставить Heapy следить за вызовом / процессом функции без изменения его источника с нулевой удачей.
Я нашел способ выполнить вашу задачу, используя встроенную библиотеку Python resource
, Обратите внимание, что в документации не указано, что RU_MAXRSS
значение возвращается. Другой пользователь SO отметил, что это было в кБ. Запуская Mac OSX 7.3 и наблюдая, как мои системные ресурсы увеличиваются во время тестового кода ниже, я считаю, что возвращаемые значения должны быть в байтах, а не в килобайтах.
10000-футовое представление о том, как я использовал resource
библиотека для мониторинга вызова библиотеки состояла в том, чтобы запустить функцию в отдельном (контролируемом) потоке и отследить системные ресурсы для этого процесса в основном потоке. Ниже у меня есть два файла, которые вам нужно будет запустить, чтобы проверить это.
Монитор ресурсов библиотеки - what_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 __name__ == "__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
Это можно сделать с помощью memory_profiler. Функция memory_usage
возвращает список значений, они представляют использование памяти с течением времени (по умолчанию более порции в 0,1 секунды). Если вам нужен максимум, просто возьмите максимум из этого списка. Маленький пример:
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))
В моем случае (memory_profiler 0.25) if выводит следующий вывод:
Memory usage (in chunks of .1 seconds): [45.65625, 45.734375, 46.41015625, 53.734375]
Maximum memory usage: 53.734375
Улучшение ответа @Vader B (так как он у меня не работал из коробки):
$ /usr/bin/time --verbose ./myscript.py
Command being timed: "./myscript.py"
User time (seconds): 16.78
System time (seconds): 2.74
Percent of CPU this job got: 117%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:16.58
Average shared text size (kbytes): 0
Average unshared data size (kbytes): 0
Average stack size (kbytes): 0
Average total size (kbytes): 0
Maximum resident set size (kbytes): 616092 # WE NEED THIS!!!
Average resident set size (kbytes): 0
Major (requiring I/O) page faults: 0
Minor (reclaiming a frame) page faults: 432750
Voluntary context switches: 1075
Involuntary context switches: 118503
Swaps: 0
File system inputs: 0
File system outputs: 800
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size (bytes): 4096
Exit status: 0
Вы можете использовать ресурс библиотеки Python, чтобы получить использование памяти.
import resource
resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
Это даст использование памяти в килобайтах, чтобы конвертировать в МБ, разделить на 1000.
Это похоже на работу под Windows. Не знаю о других операционных системах.
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
Чтение источника free
информация, /proc/meminfo
в системе Linux:
~ head /proc/meminfo
MemTotal: 4039168 kB
MemFree: 2567392 kB
MemAvailable: 3169436 kB
Buffers: 81756 kB
Cached: 712808 kB
SwapCached: 0 kB
Active: 835276 kB
Inactive: 457436 kB
Active(anon): 499080 kB
Inactive(anon): 17968 kB
Я создал класс-декоратор для измерения потребления памяти функцией.
class memoryit:
def FreeMemory():
with open('/proc/meminfo') as file:
for line in file:
if 'MemFree' in line:
free_memKB = line.split()[1]
return (float(free_memKB)/(1024*1024)) # returns GBytes float
def __init__(self, function): # Decorator class to print the memory consumption of a
self.function = function # function/method after calling it a number of iterations
def __call__(self, *args, iterations = 1, **kwargs):
before = memoryit.FreeMemory()
for i in range (iterations):
result = self.function(*args, **kwargs)
after = memoryit.FreeMemory()
print ('%r memory used: %2.3f GB' % (self.function.__name__, (before - after) / iterations))
return result
Функция измерения потребления:
@memoryit
def MakeMatrix (dim):
matrix = []
for i in range (dim):
matrix.append([j for j in range (dim)])
return (matrix)
Применение:
print ("Starting memory:", memoryit.FreeMemory())
m = MakeMatrix(10000)
print ("Ending memory:", memoryit.FreeMemory() )
Распечатка:
Starting memory: 10.58599853515625
'MakeMatrix' memory used: 3.741 GB
Ending memory: 6.864116668701172
Стандартная утилита Unix time
отслеживает максимальное использование памяти процессом, а также другую полезную статистику для вашей программы.
Пример вывода (maxresident
максимальное использование памяти, в килобайтах.):
> time python ./scalabilty_test.py
45.31user 1.86system 0:47.23elapsed 99%CPU (0avgtext+0avgdata 369824maxresident)k
0inputs+100208outputs (0major+99494minor)pagefaults 0swaps
С этой задачей тоже боролись. После экспериментов с psutil и методами от Адама я написал функцию (кредиты Адаму Льюису) для измерения памяти, используемой определенной функцией. Людям легче найти и использовать.
Я обнаружил, что материалы о многопоточности и переопределении суперкласса действительно помогают понять, что Адам делает в своих сценариях. Извините, я не могу опубликовать ссылки из-за максимального ограничения "2 ссылки".