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 · 5071 次点击
    这是一个创建于 605 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    • 第一种,耗时 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 的
    pytth
        34
    pytth  
       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   ·   我们的愿景   ·   实用小工具   ·   4979 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 121ms · UTC 09:48 · PVG 17:48 · LAX 02:48 · JFK 05:48
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.