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

2022-08-01 15:30:50 +08:00
 wyc9296

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

a = ''
for i in range(100000):
    a = a+str(i)+'->'
b = ''.join([str(i)+'->' for i in range(100000)])
c = '->'.join(map(str, range(100000)))+'->'

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

5129 次点击
所在节点    Python
39 条回复
mxT52CRuqR6o5
2022-08-01 15:35:04 +08:00
for 相比 join 会生成大量的 string 中间状态影响性能吧
sujin190
2022-08-01 15:36:33 +08:00
你这分明是内存复制的问题,和 Python 毛关系没有吧,第一个慢的循环内存申请和复制量随着循环次数增加会指数增长,啥语言都会慢很多
Mohanson
2022-08-01 15:37:41 +08:00
思考下字符串加法.
metaquant
2022-08-01 15:37:50 +08:00
这个和 python 循环的效率无关,而是因为 python 中字符串是 immutable 的,每次拼接字符串都会产生复制一个新的字符串变量,开销较大,所以涉及大量字符串拼接时,正确的做法是使用第二和第三种方法中的 join , 所以你可以发现第二和第三种方法的差距很小。
sujin190
2022-08-01 15:38:58 +08:00
如果你在 c 或者 java 直接仅仅写这么一段也很快,那大概率是被编译器展开优化掉了,关掉编译优化再看看
wxf666
2022-08-01 15:42:11 +08:00
你换成 a += str(i)+'->' 就差不多一样了
qianc1990
2022-08-01 15:42:26 +08:00
第一个时间主要花在每次循环的字符串拼接上了
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
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
2022-08-01 16:18:05 +08:00
第一个你是不是看错时间了,不是 7.8s 而是 0.78s ,我实测耗时 0.8s 左右
js 测试我跟楼上一样只需要 13ms 左右就完成循环了
wxf666
2022-08-01 16:38:33 +08:00
这个例子中,Google 花这么多钱搞的 V8 ,也没甩开 Python 多少啊?

这 8 楼 9 楼( 9 楼还少了个数量级)和楼主的没 JIT 的 Python 一比(第一个例子改成 += 就好),也没快多少啊
ipwx
2022-08-01 16:43:32 +08:00
无论什么语言,大量小字符串直接拼接都会有严重的性能问题。
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
2022-08-01 16:45:36 +08:00
@nekochyan 没错,是因为你的电脑 CPU 比较给力。我的电脑在跑的时候 4 个核中的一个已经用了 100%了...你可以把循环次数*10 ,应该就能看出来明显的差别了。
应该就是和 @metaquant 大佬所说的和 python 的不可变对象有关系。用 @wxf666 兄弟的办法`a += str(i)+'->' `可以看到耗时虽然比不上 join ,但也明显降低了。
wxf666
2022-08-01 16:48:13 +08:00
@wyc9296 你把 str(i)+'->' 改成 f'{i}->' 应该还能再快些
xgdgsc
2022-08-01 16:55:48 +08:00
就是很差的,julia 用 PythonCall.jl 用 julia 的 for 循环调用 python 函数随便比 python 快几个数量级
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
2022-08-01 17:04:37 +08:00
@xgdgsc 不会吧,就算那个测评榜,C/C++ 之类也就比 Python 快两个数量级,你这啥能 3+ 个?
wxf666
2022-08-01 17:12:43 +08:00
@houzhiqiang 看起来日用差距不大,真要求性能,还是上 C/C++ 之类的吧,wasm 都没用(比如 squoosh 转码 avif ,比本地 avifenc 慢太多)
HankLu
2022-08-01 17:14:46 +08:00
判断一个字符串是否在一个巨大的文本里面怎么样最快?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/869981

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX