Python for 循环的效率是这么差么?还是别的什么原因? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
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
wyc9296
V2EX    Python

Python for 循环的效率是这么差么?还是别的什么原因?

  •  1
     
  •   wyc9296 2022-08-01 15:30:50 +08:00 5803 次点击
    这是一个创建于 1172 天前的主题,其中的信息可能已经有所发展或是发生改变。

    三种写法,耗时从高到低,如下面代码所示,输出结果一样。各位大佬帮忙看下原因?

    • 第一种,耗时 7.688s
    a = '' for i in range(100000): a = a+str(i)+'->' 
    • 第二种,耗时 0.025s
    b = ''.join([str(i)+'->' for i in range(100000)]) 
    • 第三种,耗时 0.017s
    c = '->'.join(map(str, range(100000)))+'->' 

    输出结果:0->1->2->3->4->5->6->7->8->9...->99998->99999->

    39 条回复    2022-08-02 10:01:55 +08:00
    mxT52CRuqR6o5
        1
    mxT52CRuqR6o5  
       2022-08-01 15:35:04 +08:00
    for 相比 join 会生成大量的 string 中间状态影响性能吧
    sujin190
        2
    sujin190  
       2022-08-01 15:36:33 +08:00   1
    你这分明是内存复制的问题,和 Python 毛关系没有吧,第一个慢的循环内存申请和复制量随着循环次数增加会指数增长,啥语言都会慢很多
    Mohanson
        3
    Mohanson  
       2022-08-01 15:37:41 +08:00
    思考下字符串加法.
    metaquant
        4
    metaquant  
       2022-08-01 15:37:50 +08:00
    这个和 python 循环的效率无关,而是因为 python 中字符串是 immutable 的,每次拼接字符串都会产生复制一个新的字符串变量,开销较大,所以涉及大量字符串拼接时,正确的做法是使用第二和第三种方法中的 join , 所以你可以发现第二和第三种方法的差距很小。
    sujin190
        5
    sujin190  
       2022-08-01 15:38:58 +08:00
    如果你在 c 或者 java 直接仅仅写这么一段也很快,那大概率是被编译器展开优化掉了,关掉编译优化再看看
    wxf666
        6
    wxf666  
       2022-08-01 15:42:11 +08:00   1
    你换成 a += str(i)+'->' 就差不多一样了
    qianc1990
        7
    qianc1990  
       2022-08-01 15:42:26 +08:00
    第一个时间主要花在每次循环的字符串拼接上了
    churchill
        8
    churchill  
       2022-08-01 15:44:06 +08:00

    ```
    function test() {
    a = ''
    for (let i=0; i<100000; i++) a = a+i+'->'
    return a
    }
    test()
    console.time("python sucks")
    test()
    console.timeEnd("python sucks")
    VM809:9 python sucks: 19.73193359375 ms
    ```
    lingly02
        9
    lingly02  
       2022-08-01 16:14:05 +08:00
    function test2() {[...Array(10000).keys()].map((i)=>i+'->').join('')}
    console.time("python sucks")
    test2()
    console.timeEnd("python sucks")
    VM1332:3 python sucks: 1.7998046875 ms
    nekochyan
        10
    nekochyan  
       2022-08-01 16:18:05 +08:00
    第一个你是不是看错时间了,不是 7.8s 而是 0.78s ,我实测耗时 0.8s 左右
    js 测试我跟楼上一样只需要 13ms 左右就完成循环了
    wxf666
        11
    wxf666  
       2022-08-01 16:38:33 +08:00
    这个例子中,Google 花这么多钱搞的 V8 ,也没甩开 Python 多少啊?

    这 8 楼 9 楼( 9 楼还少了个数量级)和楼主的没 JIT 的 Python 一比(第一个例子改成 += 就好),也没快多少啊
    ipwx
        12
    ipwx  
       2022-08-01 16:43:32 +08:00
    无论什么语言,大量小字符串直接拼接都会有严重的性能问题。
    aloxaf
        13
    aloxaf  
       2022-08-01 16:43:57 +08:00
    见 wtrfpython: https://github.com/satwikkansal/wtfpython#section-miscellaneous

    顺便本机测了一下,方法一 700 ms ,换 bytearray 只要 20ms

    a = bytearray()
    for i in range(100000):
    a.extend((str(i) + '->').encode())
    a = a.decode()
    wyc9296
        14
    wyc9296  
    OP
       2022-08-01 16:45:36 +08:00
    @nekochyan 没错,是因为你的电脑 CPU 比较给力。我的电脑在跑的时候 4 个核中的一个已经用了 100%了...你可以把循环次数*10 ,应该就能看出来明显的差别了。
    应该就是和 @metaquant 大佬所说的和 python 的不可变对象有关系。用 @wxf666 兄弟的办法`a += str(i)+'->' `可以看到耗时虽然比不上 join ,但也明显降低了。
    wxf666
        15
    wxf666  
       2022-08-01 16:48:13 +08:00
    @wyc9296 你把 str(i)+'->' 改成 f'{i}->' 应该还能再快些
    xgdgsc
        16
    xgdgsc  
       2022-08-01 16:55:48 +08:00 via Android
    就是很差的,julia 用 PythonCall.jl 用 julia 的 for 循环调用 python 函数随便比 python 快几个数量级
    houzhiqiang
        17
    houzhiqiang  
       2022-08-01 17:03:10 +08:00
    ```python
    import time


    def test(n: int = 100000):
    # return '->'.join([f'{x}' for x in range(n)]) + '->'
    return '->'.join(map(str, range(n))) + '->'


    start = time.time()
    test()
    print(f"used: {(time.time() - start) * 1000}ms")
    ```

    cpython3.10.3
    time python test.py
    used: 19.52505111694336ms
    python test.py 0.05s user 0.03s system 87% cpu 0.096 total

    pypy3.9-7.3.9
    time pypy test.py
    used: 7.740020751953125ms
    pypy test.py 0.06s user 0.04s system 89% cpu 0.118 total

    #9 @lingly02 的代码
    node v16.14.2
    time node test.js
    python sucks: 3.757ms
    node test.js 0.05s user 0.02s system 95% cpu 0.069 total
    wxf666
        18
    wxf666  
       2022-08-01 17:04:37 +08:00
    @xgdgsc 不会吧,就算那个测评榜,C/C++ 之类也就比 Python 快两个数量级,你这啥能 3+ 个?
    wxf666
        19
    wxf666  
       2022-08-01 17:12:43 +08:00
    @houzhiqiang 看起来日用差距不大,真要求性能,还是上 C/C++ 之类的吧,wasm 都没用(比如 squoosh 转码 avif ,比本地 avifenc 慢太多)
    HankLu
        20
    HankLu  
       2022-08-01 17:14:46 +08:00
    判断一个字符串是否在一个巨大的文本里面怎么样最快?
    houzhiqiang
        21
    houzhiqiang  
       2022-08-01 17:17:53 +08:00
    #8 @churchill 的代码
    node v16.14.2
    time node test.js
    python sucks: 26.584ms
    node test.js 0.10s user 0.02s system 98% cpu 0.124 total

    #9 @lingly02 的代码 把他的 Array(10000)换成 Array(100000)
    node v16.14.2
    time node test.js
    python sucks: 24.985ms
    node test.js 0.06s user 0.02s system 97% cpu 0.084 total

    @houzhiqiang
    houzhiqiang
        22
    houzhiqiang  
       2022-08-01 17:19:43 +08:00
    @wxf666 cpython3.10 比 #8 @churchill 稍快一点
    wxf666
        23
    wxf666  
       2022-08-01 17:30:43 +08:00
    @houzhiqiang 嗯?我第一眼看你 17 楼,以为 v8 比 pypy 快一倍,比 cpython 快五倍呢,

    原来你是用了 少一个数量级 那个代码啊。。
    wxf666
        24
    wxf666  
       2022-08-01 17:43:12 +08:00
    @HankLu 其他帖子有大佬说过,Python 的 in 用了介于 Boyer-Moore 和 Horspool 之间的算法,比 KMP 快

    还不满足的话,你可以问问 @xgdgsc #16 ,

    按他说法,那个啥语言随便起来比 Python 快几个数量级(假设 3 个),那严肃起来还要再快 1~2 个

    有个排行榜说,C/C++ 比 Python 快 2 个数量级,

    综合算下来,那个啥语言严肃地写,可以比 C/C++ 快 1~3 个数量级
    churchill
        25
    churchill  
       2022-08-01 18:00:35 +08:00
    这。。。我就是打个趣,代码是按楼主的第一种写法原封不动搬来的
    性能这个事情 python 也还是要挣扎一下吗,大可不必
    黑魔法的话 js 也有呀
    warm 一下立杆见影
    test()
    setTimeout(() => {
    console.time("python sucks")
    test()
    console.timeEnd("python sucks")
    })
    lingly02
        26
    lingly02  
       2022-08-01 18:25:52 +08:00 via iPhone
    @houzhiqiang 上班摸鱼,看错数量了,
    wxf666
        27
    wxf666  
       2022-08-01 18:27:26 +08:00   2
    @churchill 感觉就像娱乐圈明星某些粉丝的行为,别人讨论点不相干的啥都来踩一脚。。

    “我家 xxx 最棒!”

    我不是哪个语言的粉,有更合适的就学着用
    Elaina
        28
    Elaina  
       2022-08-01 18:30:50 +08:00
    是字符串拼接影响效率,你把字符串拼接改成空操作,就没这么夸张了
    churchill
        29
    churchill  
       2022-08-01 19:20:42 +08:00
    @wxf666 伤害到你的感情了,我很抱歉,对不起,python 好棒棒
    zhuweiyou
        30
    zhuweiyou  
       2022-08-01 19:34:24 +08:00
    字符串拼接的问题
    xgdgsc
        31
    xgdgsc  
       2022-08-01 20:02:56 +08:00
    @wxf666 for 循环里先读取 memmap 的 array 里一个数,然后调用函数,这种情况反正用 julia 比 python 至少快一个数量级,我用几只不过想不起来具体快多少了,并不是说一定大于等于 3 才用几.
    HankLu
        32
    HankLu  
       2022-08-01 20:20:58 +08:00
    @wxf666 其实很满意 Python 的 in 操作,就是想找更快的
    w3cll
        33
    w3cll  
       2022-08-01 20:41:50 +08:00
    蹲一个 PHP 的
    liKeYunKeji
        34
    liKeYunKeji  
       2022-08-01 21:06:26 +08:00
    ```
    <?php

    $starttime = explode(' ',microtime());
    for($i=0; $i<100000; $i++){
    echo $i.'<br/>';
    }
    $endtime = explode(' ',microtime());
    $thistime = $endtime[0]+$endtime[1]-($starttime[0]+$starttime[1]);
    $thistime = round($thistime,3);
    echo '用时'.$thistime;

    ?>
    ```

    <img src="https://sc01.alicdn.com/kf/Ha0b06a519774459597cb42b339bd7ecbx.png" />


    @w3cll
    wxf666
        35
    wxf666  
       2022-08-02 00:23:23 +08:00
    @churchill 嗯,我痛心疾首

    那 21 楼会让你如丧考妣
    wxf666
        36
    wxf666  
       2022-08-02 00:34:25 +08:00
    @HankLu 你可以去问问 @churchill ,毕竟 js 是世界上最好的语言,写出来的代码有着 O(1/N) 复杂度,区区一个大文件算得了什么,某些机构对世界的监控就是用 js 做搜索的
    Aloento
        37
    Aloento  
       2022-08-02 02:35:47 +08:00
    @wxf666 你在说什么(((
    jinliming2
        38
    jinliming2  
       2022-08-02 08:38:11 +08:00 via iPhone
    @pytth #34 你这个 echo 是流输出啊,不是字符串拼接,有点作弊了哦……
    Anivial
        39
    Anivial  
       2022-08-02 10:01:55 +08:00
    一个提问贴能多这么多阴阳怪气的人也是没谁了,以后看见歪楼的就走,无语死
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     970 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 33ms UTC 22:40 PVG 06:40 LAX 15:40 JFK 18:40
    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