多进程队列 multiprocessing.Queue() 通过 get 方法获取数据很慢, 1000 条要 5 秒多 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
1462326016

多进程队列 multiprocessing.Queue() 通过 get 方法获取数据很慢, 1000 条要 5 秒多

  •  
  •   1462326016 Aug 16, 2019 7034 views
    This topic created in 2445 days ago, the information mentioned may be changed or developed.

    先说环境,win10 系统,1903,Python3.7.4 64 位,没有用第三方库,同样的代码在 windows (本机)上和 linux 上运行结果不一样,时间差的太大了。补充 linux 环境,Ubuntu 18.04.3 LTS,4.15.0-58-generic,Python3.6.8 64 位。 ps:windows 上 3.6.8 我也试了,还是一样的结果

    然后上代码

    # -*- coding: utf-8 -*- import random import string import threading import time import multiprocessing queue = multiprocessing.Queue() def get_str(): s = string.digits + string.ascii_letters while True: if queue.qsize() < 10000: for _ in range(10000): aaa = ''.join([random.choice(s) for _ in range(random.randint(20, 50))]) queue.put(aaa) def get_data(queue): print('insert') while True: if queue.qsize() == 0: time.sleep(2) continue start = time.time() temp = [] for _ in range(1000): temp.append(queue.get()) print(f'获取 1000 条数据需要的时间为:{time.time() - start:.2f}') time.sleep(2) def get_size(queue): print('size') while True: print(f'队列大小为:{queue.qsize()}') time.sleep(1) if __name__ == '__main__': multiprocessing.freeze_support() multiprocessing.Process(args=(queue,), target=get_data).start() threading.Thread(args=(), target=get_str).start() threading.Thread(args=(queue,), target=get_size).start() 

    windows 下运行结果(截取一部分,代码为死循环)为:

    size 队列大小为:2 insert 队列大小为:19875 队列大小为:19593 队列大小为:19457 队列大小为:19266 队列大小为:19097 获取 1000 条数据需要的时间为:5.65 队列大小为:19000 队列大小为:19000 队列大小为:18776 队列大小为:18634 队列大小为:18430 队列大小为:18283 队列大小为:18070 获取 1000 条数据需要的时间为:4.48 队列大小为:18000 队列大小为:18000 队列大小为:17689 队列大小为:17549 队列大小为:17399 队列大小为:17256 队列大小为:17093 获取 1000 条数据需要的时间为:5.55 队列大小为:17000 队列大小为:17000 队列大小为:16763 

    linux 下运行结果(截取一部分,代码为死循环)为:

    insert size 队列大小为:1 获取 1000 条数据需要的时间为:0.08 队列大小为:19000 队列大小为:19000 获取 1000 条数据需要的时间为:0.01 队列大小为:18000 队列大小为:18000 获取 1000 条数据需要的时间为:0.02 队列大小为:17000 队列大小为:17000 获取 1000 条数据需要的时间为:0.01 队列大小为:16000 队列大小为:16000 获取 1000 条数据需要的时间为:0.01 队列大小为:15000 队列大小为:15000 获取 1000 条数据需要的时间为:0.01 队列大小为:14000 队列大小为:14000 获取 1000 条数据需要的时间为:0.01 队列大小为:13000 队列大小为:13000 获取 1000 条数据需要的时间为:0.01 队列大小为:12000 队列大小为:12000 获取 1000 条数据需要的时间为:0.02 
    Supplement 1    Aug 16, 2019

    感谢大家的帮助,找到原因了,因为multiprocessing.Queue()是进程安全的队列, 所以put和get不能同时进行,会有锁机制。问题在于

    while True: i queue.qsize() < 10000: for _ in range(10000): aaa = ''.join([random.choice(s) for _ in range(random.randint(20, 50))]) queue.put(aaa) 

    这个死循环没有加sleep,导致不断地判断队列数量是否小于10000,所以可能会多次抢到锁,导致get不能及时获取数据

    21 replies    2020-03-14 23:06:01 +08:00
    lllllliu
        1
    lllllliu  
       Aug 16, 2019
    debug,对比看哪个 api 比较耗时,,然后在深入 api 看平台特性。。。。balabla 一顿操作,最后。。。。
    BingoXuan
        2
    BingoXuan  
       Aug 16, 2019
    获取 1000 条数据需要的时间为:1.31
    获取 1000 条数据需要的时间为:0.87
    获取 1000 条数据需要的时间为:0.74
    获取 1000 条数据需要的时间为:0.33
    获取 1000 条数据需要的时间为:1.03
    获取 1000 条数据需要的时间为:2.97
    获取 1000 条数据需要的时间为:2.14
    获取 1000 条数据需要的时间为:0.35
    获取 1000 条数据需要的时间为:0.34
    获取 1000 条数据需要的时间为:0.70
    获取 1000 条数据需要的时间为:0.41
    获取 1000 条数据需要的时间为:0.40

    确实有些时候是很慢,但并没有每一次都那么慢。同 windows 10,低配 xps 9570
    1462326016
        3
    1462326016  
    OP
       Aug 16, 2019
    @lllllliu 只记录了 queue 的 get 方法的耗时,所以应该就是在 get 的时候比较耗时,但是单条数据会很快,数据多了就很慢,很难 debug。。。
    1462326016
        4
    1462326016  
    OP
       Aug 16, 2019
    @BingoXuan 所以说我觉得可能是 windows 平台实现方式不一样导致的?但是我记得我之前也这么用过,是没有问题的。。。。
    BingoXuan
        5
    BingoXuan  
       Aug 16, 2019
    @1462326016
    不同平台存在性能差异是必定的,但同平台差别那么大就不会是 api 问题。我测试平均也就 1s,你的测试结果是 5s。是否可是尝试将生产者和消费者的速度控制一下,设置 timeout,每 0.1 秒生产并消费 1000 个
    skinny
        6
    skinny  
       Aug 16, 2019
    你应该先初始化队列再测试,不然给队列 put 元素的速度比不过 get 元素的速度就会造成 time.sleep(2)
    1462326016
        7
    1462326016  
    OP
       Aug 16, 2019
    @skinny put 元素的速度是很快的,你可以看 size 线程的输出值,几秒钟就可以生成 10000 条数据,并且在数据不足 10000 的时候再次生成数据 put 进去。所以说无论是否 time.sleep(2),都可以保证 get 的时候队列中数据是大于 10000 的
    1462326016
        8
    1462326016  
    OP
       Aug 16, 2019
    @BingoXuan 生产者只需保证队列中的数据达到 10000 条就不会再生产了,这时候只剩下消费者不停地 get 数据
    skinny
        9
    skinny  
       Aug 16, 2019
    @1462326016 你把 get_str 修改成初始化队列试试,获取 1000 条那就只需要 0.02 秒
    skinny
        10
    skinny  
       Aug 16, 2019
    生成随机字符串也是很花时间的
    1462326016
        11
    1462326016  
    OP
       Aug 16, 2019
    @skinny 已经找到原因了,详见附言,感谢帮助。
    gravitykey
        12
    gravitykey  
       Aug 16, 2019
    win7,py3.6

    队列大小为:181
    insert
    获取 1000 条数据需要的时间为:0.40
    获取 1000 条数据需要的时间为:0.05
    获取 1000 条数据需要的时间为:0.10
    获取 1000 条数据需要的时间为:0.38
    获取 1000 条数据需要的时间为:0.40
    获取 1000 条数据需要的时间为:0.25
    获取 1000 条数据需要的时间为:0.24
    获取 1000 条数据需要的时间为:0.12
    获取 1000 条数据需要的时间为:0.01
    获取 1000 条数据需要的时间为:0.57

    上面是正常跑,接下来尝试把进程的优先级拉高
    反而开始不稳定了

    获取 1000 条数据需要的时间为:0.14
    获取 1000 条数据需要的时间为:3.67
    获取 1000 条数据需要的时间为:0.27
    获取 1000 条数据需要的时间为:0.96
    获取 1000 条数据需要的时间为:1.71
    获取 1000 条数据需要的时间为:3.84
    获取 1000 条数据需要的时间为:3.50

    然后把优先级改为 [普通]

    获取 1000 条数据需要的时间为:0.19
    获取 1000 条数据需要的时间为:0.18
    获取 1000 条数据需要的时间为:0.04
    获取 1000 条数据需要的时间为:0.16
    获取 1000 条数据需要的时间为:0.14
    获取 1000 条数据需要的时间为:0.47
    获取 1000 条数据需要的时间为:0.20
    获取 1000 条数据需要的时间为:0.20
    获取 1000 条数据需要的时间为:0.12
    获取 1000 条数据需要的时间为:0.03
    获取 1000 条数据需要的时间为:0.04
    wuwukai007
        13
    wuwukai007  
       Aug 16, 2019
    win10

    队列大小为:188
    insert
    队列大小为:10000
    队列大小为:10000
    队列大小为:10000
    获取 1000 条数据需要的时间为:3.10
    队列大小为:10000
    队列大小为:10000
    队列大小为:10000
    队列大小为:10000
    队列大小为:10000
    队列大小为:10000
    获取 1000 条数据需要的时间为:3.85
    队列大小为:10000
    队列大小为:10000
    队列大小为:10000
    队列大小为:10000
    队列大小为:10000
    获取 1000 条数据需要的时间为:3.37
    队列大小为:10000
    队列大小为:10000
    rogwan
        14
    rogwan  
       Aug 16, 2019 via Android
    结论是 Windows 与 Linux 的进程实现方式不同造成的?
    lllllliu
        15
    lllllliu  
       Aug 16, 2019
    找到问题就是好同志,~ 恭喜
    1462326016
        16
    1462326016  
    OP
       Aug 16, 2019
    @rogwan 应该是 windows 和 linux 的多进程实现方式不同造成的,由于我忘了加 sleep,导致不停地在获取队列大小,造成一直持有锁,所以会很慢。linux 可能进程是 fork 出来的,所以加锁方式不同或者其他原因?个人看法,未求证!
    1462326016
        17
    1462326016  
    OP
       Aug 16, 2019
    @lllllliu 哈哈,谢谢,顺便给大家留一个参考
    1462326016
        18
    1462326016  
    OP
       Aug 16, 2019
    @gravitykey 可能是因为生产者不停持有锁造成的问题,详见附言,感谢回复。
    1462326016
        19
    1462326016  
    OP
       Aug 16, 2019
    @wuwukai007 可以尝试在生产者中加一个 else,在队里满 10000 时 sleep 一下,应该就正常了。否则会不停地获取队列大小,占用锁导致 get 很慢。
    cz5424
        20
    cz5424  
       Mar 13, 2020
    用 queue 和 pipe 扔了一张图片进去,都一样慢,windows 下
    1462326016
        21
    1462326016  
    OP
       Mar 14, 2020
    @cz5424 已经找到原因了, 详见附言
    About     Help     Advertise     Blog     API     FAQ     Solana     846 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 49ms UTC 21:52 PVG 05:52 LAX 14:52 JFK 17:52
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86