• Python 多进程间访问效率低,如何解决?


    前言

    最近在解决一些算法优化的问题,为了实时性要求,必须精益求精的将资源用到极致。同时对算法中一些多线程或者多进程处理。

    在对代码的调试过程中,发现在进程间队列使用耗时很长,特别是图片这种比较大的数据的时候。

    可以先看一下我下面的demo是不是符合你的场景。

    下面还有我的解决方案。

    使用进程间Queue效率问题场景

    代码样例如下,模拟从两个视频读取图片帧进行处理。

    1. #!/user/bin/env python
    2. # coding=utf-8
    3. """
    4. @project : csdn-pro
    5. @author : 剑客阿良_ALiang
    6. @file : test13.py
    7. @ide : PyCharm
    8. @time : 2022-09-13 10:47:35
    9. """
    10. import time
    11. import cv2
    12. from multiprocessing import Queue, Process
    13. def fun1(q: Queue):
    14. cap = cv2.VideoCapture("11.mp4")
    15. a = []
    16. while cap.isOpened():
    17. ret, frame = cap.read()
    18. if ret:
    19. a.append(frame)
    20. if len(a) == 25:
    21. q.put(a)
    22. a = []
    23. time.sleep(0.038)
    24. def fun2(q: Queue):
    25. cap = cv2.VideoCapture("3333333.mp4")
    26. a = []
    27. while cap.isOpened():
    28. ret, frame = cap.read()
    29. if ret:
    30. a.append(frame)
    31. if len(a) == 25:
    32. q.put(a)
    33. a = []
    34. time.sleep(0.038)
    35. def fun3(q1: Queue, q2: Queue, q3: Queue):
    36. while True:
    37. st0 = time.time()
    38. a1 = q1.get()
    39. st1 = time.time()
    40. a2 = q2.get()
    41. st2 = time.time()
    42. print("{} 耗时:{} - {}".format(time.time(), st1 - st0, st2 - st1))
    43. q3.put((a1, a2))
    44. def fun4(q3: Queue):
    45. while True:
    46. st0 = time.time()
    47. a1, a2 = q3.get()
    48. et = time.time()
    49. print("hhhh耗时: {}".format(et - st0))
    50. if __name__ == '__main__':
    51. q1 = Queue()
    52. q2 = Queue()
    53. q3 = Queue()
    54. p1 = Process(target=fun1, args=(q1,))
    55. p2 = Process(target=fun2, args=(q2,))
    56. p3 = Process(target=fun3, args=(q1, q2, q3,))
    57. p4 = Process(target=fun4, args=(q3,))
    58. p1.start()
    59. p2.start()
    60. p3.start()
    61. p4.start()
    62. p1.join()
    63. p2.join()
    64. p3.join()
    65. p4.join()

    代码说明:

    1、上面模拟每秒25帧读取图片,并传递一个25帧的图片list给到队列。

    我们看一下从queue获取图片list的效率。部分执行结果如下。

    1663139091.3648114 耗时:1.6036181449890137 - 0.1361703872680664
    hhhh耗时: 3.0635826587677
    1663139093.056612 耗时:1.5302414894104004 - 0.1615591049194336
    hhhh耗时: 1.6867034435272217
    1663139094.7388775 耗时:1.5256507396697998 - 0.1566147804260254
    hhhh耗时: 1.6849782466888428
    1663139096.36547 耗时:1.4680161476135254 - 0.15857625007629395
    hhhh耗时: 1.651228427886963
    1663139097.9867501 耗时:1.4417593479156494 - 0.179520845413208
    hhhh耗时: 1.609663963317871
    1663139099.5894623 耗时:1.4391484260559082 - 0.16356372833251953
    hhhh耗时: 1.7086796760559082
    1663139101.3031366 耗时:1.5481102466583252 - 0.16556406021118164
    hhhh耗时: 1.657604455947876
    1663139102.9448056 耗时:1.470097303390503 - 0.1715717315673828
    hhhh耗时: 1.5316739082336426
    1663139104.5233243 耗时:1.4139580726623535 - 0.16456055641174316

    Process finished with exit code -1

    可以看出我们从进程队列get数据的耗时很长,从q3中同时获取的时间如蓝色标记,远大于1秒钟。

    而整体获取图片帧的效率如红色标记,间隔时间大于1秒。

    采用管道模式解决

    这个时间间隔没法接受,我才用multiprocessing.Pipe管道来提前输入图片。

    样例代码如下:

    1. #!/user/bin/env python
    2. # coding=utf-8
    3. """
    4. @project : csdn-pro
    5. @author : 剑客阿良_ALiang
    6. @file : test13.py
    7. @ide : PyCharm
    8. @time : 2022-09-13 10:47:35
    9. """
    10. import threading
    11. import time
    12. import cv2
    13. from multiprocessing import Queue, Process, Pipe
    14. def fun1(pipe_in):
    15. cap = cv2.VideoCapture("11.mp4")
    16. while cap.isOpened():
    17. ret, frame = cap.read()
    18. if ret:
    19. ret, frame = cap.read()
    20. pipe_in.send((int(time.time()), frame))
    21. time.sleep(0.038)
    22. def fun2(pipe_in):
    23. cap = cv2.VideoCapture("3333333.mp4")
    24. while cap.isOpened():
    25. ret, frame = cap.read()
    26. if ret:
    27. ret, frame = cap.read()
    28. pipe_in.send((int(time.time()), frame))
    29. time.sleep(0.038)
    30. def fun3(pipe_rev1, pipe_rev2):
    31. def handle(pipe_rev1, q1):
    32. _cul = 0
    33. a = []
    34. while True:
    35. _t, _frame = pipe_rev1.recv()
    36. if _cul == 0:
    37. a.append(_frame)
    38. _cul = _t
    39. elif _t > _cul != 0:
    40. if len(a) != 0:
    41. q1.put(a)
    42. _cul = _t
    43. a = []
    44. a.append(_frame)
    45. elif _t == _cul != 0:
    46. a.append(_frame)
    47. q1 = Queue()
    48. q2 = Queue()
    49. threading.Thread(target=handle, args=(pipe_rev1, q1,)).start()
    50. threading.Thread(target=handle, args=(pipe_rev2, q2,)).start()
    51. while True:
    52. if not q1.empty() and not q2.empty():
    53. st0 = time.time()
    54. _f1 = q1.get()
    55. st1 = time.time()
    56. _f2 = q2.get()
    57. et = time.time()
    58. print("{} 耗时:{} - {}".format(time.time(), st1 - st0, et - st1))
    59. if __name__ == '__main__':
    60. pipe_in1, pipe_out1 = Pipe()
    61. pipe_in2, pipe_out2 = Pipe()
    62. p1 = Process(target=fun1, args=(pipe_in1,))
    63. p2 = Process(target=fun2, args=(pipe_in2,))
    64. p3 = Process(target=fun3, args=(pipe_out1, pipe_out2,))
    65. p1.start()
    66. p2.start()
    67. p3.start()
    68. p1.join()
    69. p2.join()
    70. p3.join()

    代码说明:

    1、通过两个线程不停从管道接收并写到内存的Queue里面,提前放到当前进程内存里。

    看一下间隔是否稳定,部分执行结果如下

    1663139886.0722673 耗时:0.003930091857910156 - 0.005983591079711914
    1663139887.6837587 耗时:0.09677457809448242 - 0.09172177314758301
    1663139888.472634 耗时:0.061833858489990234 - 0.05984067916870117
    1663139889.5441313 耗时:0.07132482528686523 - 0.07080578804016113
    1663139890.548978 耗时:0.06183457374572754 - 0.06881546974182129
    1663139891.5112402 耗时:0.0637204647064209 - 0.0718080997467041
    1663139892.4756596 耗时:0.06682205200195312 - 0.06978344917297363
    1663139893.5788367 耗时:0.06779074668884277 - 0.07928323745727539

    时间间隔还是比较稳定的。

    总结

    如果你遇到和我一样的场景,可以仔细观察一下进程间数据是否传输的比较慢。可以考虑和我一样的方式来解决。

    分享:

            人的一生有一个半童年。一个童年在自己小时候,而半个童年在自己孩子的小时候。

  • 相关阅读:
    Java格式化类Format
    Covert Communication 与选择波束(毫米波,大规模MIMO,可重构全息表面)
    [ C++ ] STL_stack(栈)queue(队列)使用及其重要接口模拟实现
    【GEE笔记1】Landsat8/9的NDVI计算,并计算区域NDVI平均值
    运行jar时提示缺少依赖的类
    Parallel Context Windows for Large Language Models
    #php的pecl工具#
    idea,web开发中jsp页面中不提示控制层的请求地址
    【C++实战】 语言特性
    07JVM_内存模型和CAS与原子类
  • 原文地址:https://blog.csdn.net/zhiweihongyan1/article/details/126851924