我读过这个回复,它解释了 CPython 有一个优化,可以在使用a = a + bor附加到字符串时执行就地附加,无需复制a += b。我还阅读了这个 PEP8 建议:
\n\n代码的编写方式不应损害其他 Python 实现(PyPy、Jython、IronPython、Cython、Psyco 等)。例如,不要依赖 CPython\xe2\x80\x99 对形式 a += b 或 a = a + b 中的语句进行就地字符串连接的高效实现。即使在 CPython 中,这种优化也是脆弱的(它仅适用于某些类型),并且在不使用引用计数的实现中根本不存在。在库的性能敏感部分,应使用\'\'.join() 形式。这将确保在各种实现中以线性时间进行串联。
\n
因此,如果我理解正确,a += b + c正确的方法是调用a = \'\'.join([a, b, c])?
但是为什么这个形式join比本例中的形式慢得多+=(在loop1中我故意使用a = a + b + c以避免触发CPython优化)?
import os\nimport time\n\nif __name__ == "__main__":\n start_time = time.time()\n print("begin: %s " % (start_time))\n s = ""\n for i in range(100000):\n s = s + str(i) + \'3\'\n time1 = time.time()\n print("end loop1: %s " % (time1 - start_time))\n\n s2 = ""\n for i in range(100000):\n s2 += str(i) + \'3\'\n\n time2 = time.time()\n print("end loop2: %s " % (time2 - time1))\n\n s3 = ""\n for i in range(100000):\n s3 = \'\'.join([s3, str(i), \'3\'])\n\n time3 = time.time()\n print("end loop3: %s " % (time3 - time2))\nRun Code Online (Sandbox Code Playgroud)\n结果显示join在这种情况下速度明显慢:
~/testdir$ python --version\nPython 3.10.6\n~/testdir$ python concatenate.py \nbegin: 1675268345.0761461 \nend loop1: 3.9019 \nend loop2: 0.0260 \nend loop3: 0.9289 \nRun Code Online (Sandbox Code Playgroud)\n难道是我的版本join不对?
join()在“loop3”中,您通过以不需要的方式连续调用它而绕过了很多增益。最好join()一次性建立完整的字符列表。
查看:
\nimport time\n\niterations = 100_000\n\n##----------------\ns = ""\nstart_time = time.time()\nfor i in range(iterations):\n s = s + "." + \'3\'\nend_time = time.time()\nprint("end loop1: %s " % (end_time - start_time))\n##----------------\n\n##----------------\ns = ""\nstart_time = time.time()\nfor i in range(iterations):\n s += "." + \'3\'\nend_time = time.time()\nprint("end loop2: %s " % (end_time - start_time))\n##----------------\n\n##----------------\ns = ""\nstart_time = time.time()\nfor i in range(iterations):\n s = \'\'.join([s, ".", \'3\'])\nend_time = time.time()\nprint("end loop3: %s " % (end_time - start_time))\n##----------------\n\n##----------------\ns = []\nstart_time = time.time()\nfor i in range(iterations):\n s.append(".")\n s.append("3")\ns = "".join(s)\nend_time = time.time()\nprint("end loop4: %s " % (end_time - start_time))\n##----------------\n\n##----------------\ns = []\nstart_time = time.time()\nfor i in range(iterations):\n s.extend((".", "3"))\ns = "".join(s)\nend_time = time.time()\nprint("end loop5: %s " % (end_time - start_time))\n##----------------\nRun Code Online (Sandbox Code Playgroud)\n需要明确的是,您可以使用以下命令运行它:
\niterations = 10_000_000\nRun Code Online (Sandbox Code Playgroud)\n如果您愿意,请务必删除“loop1”和“loop3”,因为它们在大约 300k 后会变得非常慢。
\n当我运行 1000 万次迭代时,我看到:
\nend loop2: 16.977502584457397 \nend loop4: 1.6301295757293701 \nend loop5: 1.0435805320739746\nRun Code Online (Sandbox Code Playgroud)\n所以,显然有一种join()快速使用的方法:-)
附录:
\n@\xc3\x89tienne 建议使字符串附加更长的时间会逆转结果,并且除非在函数中,否则不会发生循环 2 的优化。我没有看到相同的情况。
\nend loop2: 16.977502584457397 \nend loop4: 1.6301295757293701 \nend loop5: 1.0435805320739746\nRun Code Online (Sandbox Code Playgroud)\n在 python 3.10 和 3.11 上结果相似。我得到如下结果:
\nend loop2: 336.98531889915466 \nend loop4: 1.0211727619171143 \nend loop5: 1.1640543937683105\nRun Code Online (Sandbox Code Playgroud)\n这继续向我表明速度join()要快得多。
| 归档时间: |
|
| 查看次数: |
1484 次 |
| 最近记录: |