LINUX.ORG.RU

memory leak в multiprocessing

 , ,


0

1

Почему при передачи кадров подпроцессу питон сжирает всю память? Причем если передавать например счетчик i то утечки нет.

from multiprocessing import Process, Queue
from pympler.tracker import SummaryTracker
import cv2


def worker(inputQueue, outputQueue):
	while True:
		if not inputQueue.empty():
			frame = inputQueue.get()

inputQueue = Queue(maxsize=1)
outputQueue = Queue(maxsize=1)

vs = cv2.VideoCapture(0)

p = Process(target=worker, args=(inputQueue, outputQueue))
p.daemon = True
p.start()
i = 0
tracker = SummaryTracker()

while True:
	ret, frame = vs.read()
	if ret == True:
		i += 1
		if inputQueue.empty():
			inputQueue.put(frame)	# <--утечка тут!
		cv2.imshow("Camera", frame)

		if i == 100:
			tracker.print_diff()
			i = 0

	if cv2.waitKey(1) & 0xFF == ord("q"):
		break

p.terminate()
vs.release()
cv2.destroyAllWindows()

Спасибо!

Это у тебя какое-то легаси из 2005. Пальцем в небо ты передаёшь объект вместе с потрошками вместо только нужной информации и сборщик мусора не может удалить тебе лишнее. Внешне это может быть не заметно, натрави type() я не знаю.

anonymous ()
Ответ на: комментарий от anonymous

О великий анон, чуть больше инфы пожалуйста, я не программист. Добавил я строку в цикл:

print(type(frame))

выдало:

<class 'numpy.ndarray'>
Что мне с этим делать?

Если что, предоставленный в посте кусок кода - это выжимка из скрипта по обнаружению человека в кадре.

lucky_guy ★★★ ()

С виду ничего утекать не должно. Запустил, за минуту ничего подозрительного.

Ты точно жалуешься на память, а не на CPU? Если сжирается весь процессор, то после inputQueue.get() сделай sleep.

from multiprocessing import Process, Queue
import cv2
import time


def worker(inputQueue, outputQueue):
  while True:
    if not inputQueue.empty():
      frame = inputQueue.get()
      time.sleep(0.1)


inputQueue = Queue(maxsize=1)
outputQueue = Queue(maxsize=1)

vs = cv2.VideoCapture(0)

p = Process(target=worker, args=(inputQueue, outputQueue))
p.daemon = True
p.start()
i = 0

while True:
  ret, frame = vs.read()
  if ret == True:
    i += 1
    if inputQueue.empty():
      inputQueue.put(frame)  # <--утечка тут!
    cv2.imshow("Camera", frame)

    if i == 100:
      print('Restart counter ' + str(i))
      i = 0

  if cv2.waitKey(1) & 0xFF == ord("q"):
    break

p.terminate()
vs.release()
cv2.destroyAllWindows()

Или, как вариант, утечка где-то в SummaryTracker.

И давай подробности. Работает на Raspberry Pi? Сколько памяти утекает? Как быстро?

anonymous ()
Ответ на: комментарий от anonymous

Ты точно жалуешься на память, а не на CPU?

Разумеется память.

Или, как вариант, утечка где-то в SummaryTracker.

Точно нет.

И давай подробности. Работает на Raspberry Pi? Сколько памяти утекает? Как быстро?

На малинке 3 модель b, утекает вся пока система ни зависнет. Скорость зависит от активности передачи кадров подпроцессу, если в его цикле вставить паузу то утечка замедляется, но не останавливается.

Вот код с визуализацией памяти.

from multiprocessing import Process, Queue
from pympler import muppy, summary
import numpy as np
import cv2
import time
import os
import array
import objgraph


def worker(inputQueue,):
	while True:
		if not inputQueue.empty():
			frame = inputQueue.get()

vs = cv2.VideoCapture(0)
i = 0

inputQueue = Queue()
p = Process(target=worker, args=(inputQueue,))
p.daemon = True
p.start()

while True:
	ret, frame = vs.read()
	if ret == True:
		
		if inputQueue.empty():
			inputQueue.put(frame)
		
		cv2.imshow("Camera", frame)

		i += 1
		if i == 300:
			i = 0
			summary.print_(summary.summarize(muppy.get_objects()))
			all_objects = muppy.get_objects()
			all_arrays = [obj for obj in all_objects if isinstance(obj, array.array)]
			objgraph.show_backrefs(all_arrays[0], filename='array.png')

	if cv2.waitKey(1) & 0xFF == ord("q"):
		break

p.terminate()
vs.release()
cv2.destroyAllWindows()

Изображение полученное через минуту. Через 1.5 минуты свободная память исчерпывается.

lucky_guy ★★★ ()
Ответ на: комментарий от lucky_guy

У себя проверить не могу - на Rapberry камеры нет, на ноуте ничего не утекает.

Можешь запустить безо всякого мультипроцессинга и очереди (убрать if inputQueue.empty(): inputQueue.put(frame) ), утекает ли память?

anonymous ()
Ответ на: комментарий от anonymous

В первой строчке поста я написал что если передавать в очередь что-то другое кроме кадров(например счетчик), то утечки нет.

Либо закомментировать строку

inputQueue.put(frame)

lucky_guy ★★★ ()
Ответ на: комментарий от lucky_guy

А питон третий, или второй? В цикле читал картинку из файла, а не камеры - никакой утечки не увидел.

Raspberry PI 3B+, Raspbian 9.4, Python 3.5.3, opencv-python 3.4.1.15, numpy 1.12.1 .

В любом случае, изображение можно писать на диск, а не кидать в очередь. А на диске подмонтировать папку в tmpfs, и вообще никаких проблем не будет. Два момента. Чтение/запись нужно защитить блокировкой. И при записи jpg-файла будут потери от сжатия. Можно писать *.png, но для этого нужно больше памяти, по-моему, это лишнее.

from multiprocessing import Process, Queue, Lock
# from pympler.tracker import SummaryTracker
import cv2
import subprocess

# При записи в файл будут потери от джепега, но это не страшно.
# если не жалко места в tmpfs, то можно сменить расширение на png.
img_file = 'tmp/screenshot.jpg'


def init_fs():
  #subprocess.call('sudo umount tmp', shell=True)
  #subprocess.call('sudo mount -t tmpfs -o size=20m tmpfs tmp', shell=True)
  subprocess.call('mkdir -p tmp', shell=True)




def worker(inputQueue, lock_obj, outputQueue):
  while True:
    if not inputQueue.empty():
      lock_obj.acquire()
      try:
        # frame = inputQueue.get()
        i = inputQueue.get()
        frame = cv2.imread(img_file)
      finally:
        lock_obj.release()

inputQueue = Queue(maxsize=1)
outputQueue = Queue(maxsize=1)
# Работу с диском ведем под блокировкой
lock_obj = Lock()

vs = cv2.VideoCapture(0)

p = Process(target=worker, args=(inputQueue, lock_obj, outputQueue))
p.daemon = True
p.start()
i = 0
# tracker = SummaryTracker()

init_fs()

while True:
  ret, frame = vs.read()
  if ret == True:
    lock_obj.acquire()
    try:
      cv2.imwrite(img_file, frame)
    finally:
      lock_obj.release()

    i += 1
    if inputQueue.empty():
      #inputQueue.put(frame)	# <--утечка тут!
      inputQueue.put(i)
    cv2.imshow("Camera", frame)

    if i == 100:
      # tracker.print_diff()
      i = 0

  if cv2.waitKey(1) & 0xFF == ord("q"):
    break

p.terminate()
vs.release()
cv2.destroyAllWindows()
anonymous ()