为什么变量 1 += 变量 2 比变量 1 = 变量 1 + 变量 2 快得多?

2024-06-29

我继承了一些 Python 代码,用于创建巨大的表(最多 19 列宽,5000 行)。花了九秒用于在屏幕上绘制表格。我注意到每一行都是使用以下代码添加的:

sTable = sTable + '\n' + GetRow()

where sTable是一个字符串。

我将其更改为:

sTable += '\n' + GetRow()

我注意到该表现在出现在六秒.

然后我把它改成了:

sTable += '\n%s' % GetRow()

基于这些 Python 性能技巧 https://wiki.python.org/moin/PythonSpeed/PerformanceTips#String_Concatenation(还有六秒)。

由于该函数被调用了大约 5000 次,因此凸显了性能问题。但为什么差别这么大呢?为什么编译器没有在第一个版本中发现问题并进行优化?


这与就地使用无关+= versus +二进制添加。你没有告诉我们整个故事。您的原始版本连接了 3 个字符串,而不仅仅是两个:

sTable = sTable + '\n' + sRow  # simplified, sRow is a function call

Python 尝试帮助并优化字符串连接;两者在使用时strobj += otherstrobj and strobj = strobj + otherstringobj,但是当涉及超过 2 个字符串时,它无法应用此优化。

Python 字符串是不可变的normally,但如果没有其他对左侧字符串对象的引用and无论如何它都会被反弹,然后Python会作弊并且改变字符串。这避免了每次连接时都必须创建新字符串,这可以大大提高速度。

这是在字节码评估循环中实现的。使用时两者都BINARY_ADD在两根弦上 http://hg.python.org/cpython/file/23a60d89dbd4/Python/ceval.c#l1202以及使用时INPLACE_ADD在两根弦上 http://hg.python.org/cpython/file/23a60d89dbd4/Python/ceval.c#l1406, Python 将串联委托给特殊的辅助函数string_concatenate() http://hg.python.org/cpython/file/23a60d89dbd4/Python/ceval.c#l4507。为了能够通过改变字符串来优化串联,首先需要确保该字符串没有其他引用;如果只有堆栈和原始变量引用它那么就可以做到这一点,and the next操作将替换原始变量引用。

因此,如果只有 2 个对该字符串的引用,并且下一个运算符是其中之一STORE_FAST(设置局部变量),STORE_DEREF(设置由封闭函数引用的变量)或STORE_NAME(设置一个全局变量),并且受影响的变量当前引用相同的字符串,然后清除该目标变量以将堆栈的引用数量减少到 1。

这就是为什么您的原始代码无法充分使用此优化的原因。你的表达的第一部分是sTable + '\n'next操作是another BINARY_ADD:

>>> import dis
>>> dis.dis(compile(r"sTable = sTable + '\n' + sRow", '<stdin>', 'exec'))
  1           0 LOAD_NAME                0 (sTable)
              3 LOAD_CONST               0 ('\n')
              6 BINARY_ADD          
              7 LOAD_NAME                1 (sRow)
             10 BINARY_ADD          
             11 STORE_NAME               0 (sTable)
             14 LOAD_CONST               1 (None)
             17 RETURN_VALUE        

首先BINARY_ADD后面跟着一个LOAD_NAME访问sRow变量,而不是存储操作。这第一BINARY_ADD必须始终产生一个新的字符串对象,该对象更大sTable增长并且创建这个新的字符串对象需要越来越多的时间。

您将此代码更改为:

sTable += '\n%s' % sRow

which 删除了第二个串联。现在字节码是:

>>> dis.dis(compile(r"sTable += '\n%s' % sRow", '<stdin>', 'exec'))
  1           0 LOAD_NAME                0 (sTable)
              3 LOAD_CONST               0 ('\n%s')
              6 LOAD_NAME                1 (sRow)
              9 BINARY_MODULO       
             10 INPLACE_ADD         
             11 STORE_NAME               0 (sTable)
             14 LOAD_CONST               1 (None)
             17 RETURN_VALUE        

我们剩下的就是INPLACE_ADD接下来是一家商店。现在sTable可以就地更改,不会导致更大的新字符串对象。

你会得到相同的速度差异:

sTable = sTable + ('\n%s' % sRow)

here.

计时赛显示了差异:

>>> import random
>>> from timeit import timeit
>>> testlist = [''.join([chr(random.randint(48, 127)) for _ in range(random.randrange(10, 30))]) for _ in range(1000)]
>>> def str_threevalue_concat(lst):
...     res = ''
...     for elem in lst:
...         res = res + '\n' + elem
... 
>>> def str_twovalue_concat(lst):
...     res = ''
...     for elem in lst:
...         res = res + ('\n%s' % elem)
... 
>>> timeit('f(l)', 'from __main__ import testlist as l, str_threevalue_concat as f', number=10000)
6.196403980255127
>>> timeit('f(l)', 'from __main__ import testlist as l, str_twovalue_concat as f', number=10000)
2.3599119186401367

这个故事的寓意是,您首先不应该使用字符串连接。从其他字符串的负载构建新字符串的正确方法是使用列表,然后使用str.join():

table_rows = []
for something in something_else:
    table_rows += ['\n', GetRow()]
sTable = ''.join(table_rows)

这仍然更快:

>>> def str_join_concat(lst):
...     res = ''.join(['\n%s' % elem for elem in lst])
... 
>>> timeit('f(l)', 'from __main__ import testlist as l, str_join_concat as f', number=10000)
1.7978830337524414

但你不能仅仅使用'\n'.join(lst):

>>> timeit('f(l)', 'from __main__ import testlist as l, nl_join_concat as f', number=10000)
0.23735499382019043
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么变量 1 += 变量 2 比变量 1 = 变量 1 + 变量 2 快得多? 的相关文章

  • 有没有办法可以保留子线程的上下文局部变量?

    目前 我创建了一个库来记录后端调用 例如对boto3 and requests库 然后根据一些数据 例如响应的状态代码等 填充全局 数据 对象 我原来有data对象作为全局的 但后来我意识到这是一个坏主意 因为当应用程序并行运行时 data
  • 提高mmap memcpy文件读取性能

    我有一个从文件中顺序读取数据的应用程序 有些是直接从指向的指针读取mmaped 文件和其他部分是memcpyed 从文件到另一个缓冲区 我注意到在进行大型操作时性能不佳memcpy我需要的所有内存 1MB 块 以及在执行大量较小操作时的更好
  • 以编程方式使用的免费单词列表? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 使用 Matplotlib 和 TeX 实现均匀间距

    我正在为数学课绘制一些图表 但我无法在绘图图例中正确地获得和平定义的间距 我目前正在使用 对于 TeX 中的单个空间 但会遇到一种情况 其中一个空间比另一个空间稍远 这可能是由于左边的方程占用了多少空间 这是我的代码 import matp
  • 有效地将字符串(或元组)转换为 ctypes 数组

    我有一段代码 它采用 PIL 图像并将其转换为 ctypes 数组以传递给 C 函数 w px h px img size pixels struct unpack dI w px h px img convert RGBA tostrin
  • 如何防止 python 请求对我的 URL 进行百分比编码?

    我正在尝试使用 python 中的 requests get 获取以下格式的 URL usr local bin python import requests print requests versiom url http api exam
  • 如何在条形图上添加值标签

    我正在创建一个条形图 但我不知道如何在条形图上添加值标签 在条形图的中心或正上方 我相信解决方案是使用 文本 或 注释 但我 a 不知道该使用哪一个 一般来说 还没有弄清楚何时使用哪一个 b 无法看到任何一个来呈现值标签 这是我的代码 im
  • 使用 scipy 在 python 中读取 MatLab 文件

    我正在使用 python 和 scipy 包来读取 MatLab 文件 然而 它需要太长时间并且崩溃 The Dataset http realitycommons media mit edu RealityMining zip大小约为50
  • Scikit-learn 中的 GridSearchCV 输出问题

    我想执行超参数搜索以在 sklearn 中选择预处理步骤和模型 如下所示 pipeline Pipeline combiner PolynomialFeatures dimred PCA classifier RandomForestCla
  • 设置ntlk代理

    我正在关注第一章NLTK书 http www nltk org book ch01 html frequency distributions 它要求我们通过运行来安装图书语料库nltk dowwnload 我正进入 状态getattrinf
  • 使用 pip freeze 安装 numpy

    I need to install Numpy version 1 17 1 but every time it just freezes I have now tried multiple times and I have been wa
  • 在Python中根据等级和花色对一手牌进行排序

    我正打算制作一款纸牌游戏 目前我正在着手开发它 我感到困惑的是 按牌的等级对手中的牌进行排序 然后按花色排序 以及如何减少重复 目前 我可能可以创建一个 for 循环来组织卡片 然后为每种可能性设置 52 个不同的 if 但我想知道它们是否
  • f2py:公开“已使用”模块的参数

    我认为这个问题已经在某个地方得到解决 但我花了大量的时间四处寻找答案 包括深入研究源代码 我试图将问题放在第一段中 其余部分显示了问题的基本示例 我正在尝试编译一个包含USE指向另一个更通用的模块的语句 我更愿意将使用的模块分开 以便它可以
  • 持久子进程.Popen 会话

    我正在尝试运行一个命令 然后在同一环境中运行另一个命令 比如说 如果我在第一个命令中设置环境变量 我希望它可用于第二个命令 我试过这个 import subprocess process subprocess Popen echo test
  • 关于ListView中ViewHolder模式实现优化

    因此 众所周知的 ViewHolder 模式通常看起来像 ListAdapter Override public View getView final int position View convertView final ViewGrou
  • Visual Studio 2013 中的 JavaScript HTML5 CSS3 项目模板

    我刚刚下载了 Visual Studio 2013 Express for Web 我想知道如何部署仅限 JavaScript HTML5 和 CSS3 的项目 我找不到合适的模板或方法来做到这一点 我已经在网上搜索过JavaScript模
  • JavaScript 作为 HTML 属性是不好的做法吗?

    例子 https stackoverflow com a 372 89566 710887 https stackoverflow com a 37289566 710887 我看到这种情况越来越频繁地发生 我总是被教导要将 javascr
  • Scrapy在使用crawlerprocess运行时抛出错误

    我用 python 编写了一个脚本 使用 scrapy 来收集网站上不同帖子的名称及其链接 当我从命令行执行脚本时 它可以完美地工作 现在 我的意图是使用运行脚本CrawlerProcess 我在不同的地方寻找类似的问题 但我找不到任何直接
  • 如何在javascript中获取表中复选框的值

    我需要获取表行中提供跨度的复选框的值 下面的代码是我的项目的一部分 HTML 代码用于动态我的表格 而 javascript 代码用于获取不适用于复选框的元素的值 它适用于其他输入元素 我的桌子 var html tr class rows
  • 使用具有阿拉伯字符的 json.dumps 将字典转换为 json [重复]

    这个问题在这里已经有答案了 我有一本包含阿拉伯语单词的字典 例如 data name name print json dumps data file open data json a encoding utf 8 Output name u

随机推荐

  • 矩阵行列式算法 C++

    我是编程新手 我一直在寻找一种找到矩阵行列式的方法 我在网上找到了这段代码 但我很难理解这里的算法 我对递归的基础没有问题 但继续和主循环我很难理解 非常感谢任何可以向我解释该算法的人 int determ int a MAX MAX in
  • PC 上 XNA 中的信箱和缩放

    有没有一种方法可以让我基本上以 1080p 或 720p 作为默认分辨率来开发 XNA 游戏 然后根据设置的分辨率将游戏中的所有内容缩放到适当的大小 而不必在每个 Sprite 中设置缩放因子Draw 方法 我的想法是 我可以基于 1080
  • Scala 不可变 Map 速度慢

    当我创建地图时 我有一段代码 val map gtfLineArr 8 split map split collect case Array k v gt k v toMap 然后我使用这张地图来创建我的对象 case class MyOb
  • Arduino 以太网扩展板与套接字服务器的连接

    我使用 Arduino 的以太网屏蔽将其连接到套接字服务器 不同的计算机 以便我可以从它接收消息来激活某些例程 这是我的代码 include
  • Task.Delay 到底是如何工作的?

    他们说 Task Delay 是一个异步 Thread Sleep 为了测试这一点 我写了下面的代码 我希望立即打印 One 然后 3 秒后将打印结果变量 15 2 秒后 将打印 Two 但似乎并非如此 一 不会立即打印 3 秒后打印 On
  • 更快的 WinSock sendto()

    我使用的是 Windows Server 2008 我的程序是用 C 编写的 我在 while true 循环中使用 WinSock2 和 sendto 来发送数据包 代码如下 while true if c snd gt max c sn
  • 64 位随机生成器种子

    我目前正在运行一个具有 8 个以上管道 线程 的多线程模拟应用程序 这些管道运行非常复杂的代码 该代码取决于种子生成的随机序列 然后该序列被归结为单个 0 1 我希望在将种子从主线程传递到处理管道后 这种 随机处理 具有 100 的确定性
  • python3-numpy:使用 numpy savetxt 附加到文件

    我正在尝试使用 numpy 的 savetxt 函数将数据附加到文件中 下面是最小的工作示例 usr bin env python3 import numpy as np f open asd dat a for iind in range
  • 单击即可切换背景颜色和过渡

    这看起来应该很容易 但我真的找不到办法做到这一点 动画 http doir ir css gif http doir ir css gif 当您单击这些相应的链接时 我需要更改和过渡页面的背景颜色 我见过的最接近触发这种类型转换的事情是 仅
  • 使用反射检测属性的访问修饰符类型

    我编写了一些代码来使用反射查看属性 我已经使用反射从类中检索了属性列表 但是我需要查明该财产是公共的还是受保护的 例如 public string Name get set protected int Age get set Propert
  • 如何在数据库中存储世界各地的所有地理位置?

    我在一家旅游网站工作 我需要存储游客去过的旅游景点 我需要位置表中的地点是唯一的 以便我可以知道特定地点的受欢迎程度等 我还需要存储在我身边的所有国家 州 城市 因为我不能依赖用户输入 数据库是MySQL 看到这些位置的可用数据集 我发现存
  • 如何使 Bootstrap 响应式布局在 IE8 上工作

    我已经搜索了一段时间 发现有些人可以正常工作 但没有人提供任何代码示例 我尝试了他们的建议 但对我来说没有用 根据建议 我尝试添加 respond js or css3 mediaqueries js 但他们都没有帮助 这是一个jsfidd
  • Angular2模板参考变量和动态更新

    我最初试图在 Angular2 中设置一个模板引用变量 这样我就可以反转表的排序顺序 而不必进行绑定 但当我单击复选框时 我没有得到界面的动态更新 我创建了一个简单的 plunker 以防应用程序中的其他内容可能会扰乱代码 并且我看不到正在
  • 如何在不创建新对象的情况下更新用户对象?

    以下在 shell 中工作正常 gt gt gt from django contrib auth models import User gt gt gt user User objects get pk 1 gt gt gt user f
  • 如何从索引文件迭代多个导入的模块

    我有一个名为Polygons我在那里创建了一个index jsfile 以导出目录中的所有文件 它看起来像这样 export default as europe from europe export default as northAmer
  • 如何将 Apollo 客户端与 AppSync 结合使用?

    AppSync 使用基于 WebSocket 的 MQTT 进行订阅 而 Apollo 使用 WebSocket 两者都不Subscription组件或subscribeForMore in Query当我将 apollo 与 AppSyn
  • 在 Woocommerce 中设置购物车商品价格后重新计算总计

    通过 set price 方法更改产品价格后 如何更改小计价格 现在在review order php中按旧价格计算总成本 cart php foreach WC gt cart gt get cart as cart item key g
  • Android 位图内存问题 - 错误:8294416 字节分配内存不足

    我目前正在开发一个讲述故事的应用程序 该故事包含 场景 其中包含通过 ImageView 显示的多个 JPEG 和 PNG 文件 我创建 ImageView 并通过以下函数将其添加到布局中 private ImageView newImag
  • 为什么我编写的 npm 模块在使用 create-react-app 创建项目后会安装这么多包?

    我写了这个 npm 模块 反应心跳 https www npmjs com package react heartbeat using nwb https github com insin nwb 当我在新项目中安装此模块时 npm i r
  • 为什么变量 1 += 变量 2 比变量 1 = 变量 1 + 变量 2 快得多?

    我继承了一些 Python 代码 用于创建巨大的表 最多 19 列宽 5000 行 花了九秒用于在屏幕上绘制表格 我注意到每一行都是使用以下代码添加的 sTable sTable n GetRow where sTable是一个字符串 我将