当前位置: 首页 > 知识库问答 >
问题:

s=s c字符串conat优化是如何决定的?

朱兴安
2023-03-14

简短版本:如果s是一个字符串,那么s=s“c”可能会在适当的位置修改字符串,而t=s“c”不能。但是操作如何知道它在哪种情况下?

长版本:

t=s“c”需要创建一个单独的字符串,因为程序随后希望旧字符串为s,新字符串为t。

如果s是唯一的参考,则s=s“c”可以修改字符串,因为程序只希望s是扩展字符串。如果末尾有多余的字符空间,CPython实际上会进行这种优化。

考虑这些函数,它们重复添加一个字符:

def fast(n):
    s = ''
    for _ in range(n):
        s = s + 'c'
        t = s
        del t

def slow(n):
    s = ''
    for _ in range(n):
        t = s + 'c'
        s = t
        del t

使用n=100_000对结果进行基准测试(在线尝试!):

fast :   9 ms    9 ms    9 ms    9 ms   10 ms 
slow : 924 ms  927 ms  931 ms  933 ms  945 ms

请注意,额外的t=s或s=t使这两个变量都等效于字符串的引用,然后del t只剩下s,因此在下一次循环迭代中,s再次是字符串的唯一引用。因此,这两个函数之间的唯一区别是分配给s和t的顺序。

我们还要反汇编字节码。我用标记了仅有的三个差异= 在中间。正如预期的那样,只有STORE\u FAST和LOAD\u FAST的变量不同。但是,直到并包括BINARY\u ADD,字节码是相同的。那么,BINARY\u ADD如何知道是否要优化呢?

      import dis                                 import dis
      dis.dis(fast)                              dis.dis(slow)
---------------------------------------------------------------------------
    0 LOAD_CONST     1 ('')                    0 LOAD_CONST     1 ('')
    2 STORE_FAST     1 (s)                     2 STORE_FAST     1 (s)
                                                                 
    4 LOAD_GLOBAL    0 (range)                 4 LOAD_GLOBAL    0 (range)
    6 LOAD_FAST      0 (n)                     6 LOAD_FAST      0 (n)
    8 CALL_FUNCTION  1                         8 CALL_FUNCTION  1
   10 GET_ITER                                10 GET_ITER      
>> 12 FOR_ITER      18 (to 32)             >> 12 FOR_ITER      18 (to 32)
   14 STORE_FAST     2 (_)                    14 STORE_FAST     2 (_)
                                                               
   16 LOAD_FAST      1 (s)                    16 LOAD_FAST      1 (s)
   18 LOAD_CONST     2 ('c')                  18 LOAD_CONST     2 ('c')
   20 BINARY_ADD                              20 BINARY_ADD    
   22 STORE_FAST     1 (s)        !=          22 STORE_FAST     3 (t)
                                                               
   24 LOAD_FAST      1 (s)        !=          24 LOAD_FAST      3 (t)
   26 STORE_FAST     3 (t)        !=          26 STORE_FAST     1 (s)
                                                               
   28 DELETE_FAST    3 (t)                    28 DELETE_FAST    3 (t)
   30 JUMP_ABSOLUTE 12                        30 JUMP_ABSOLUTE 12
>> 32 LOAD_CONST     0 (None)              >> 32 LOAD_CONST     0 (None)
   34 RETURN_VALUE                            34 RETURN_VALUE  

共有1个答案

聂和宜
2023-03-14

下面是有问题的代码,来自Python 3.10分支(在ceval.c中,并从同一文件的二进制添加操作码实现中调用)。正如@jasonharper在一篇评论中所指出的,它会向前看,看看二进制加法的结果是否会被绑定到左手加数所在的名称上。在快速(code)中,它是(操作数来自s,结果存储到s中),但在慢速(code)中它不是(操作数来自s,但存储到t中)。

不过,不能保证这种优化会持续下去。例如,我注意到您的fast()并不比当前开发的CPythonmain分支上的slow()快(这是目前正在进行的最终3.11版本的工作)。

如前所述,不能保证这种优化会持续下去。“严肃的”Python程序员应该知道不要依赖不可靠的CPython特定技巧,事实上,PEP 8明确警告不要依赖这个特定的技巧:

代码的编写方式应不影响其他Python实现(PyPy、Jython、IronPython、Cython、Psyco等)。

例如,不要依赖CPython对a=ba=a b形式的语句的就地字符串连接的有效实现...

 类似资料:
  • 问题内容: 希望这只是一个简单的问题,涉及到Sql 2008中的查询时的性能优化。 我曾为在其ETL流程以及一些网站中经常使用Stored Procs的公司工作。我已经看到了他们需要基于一组有限的键值来检索特定记录的情况。我已经看到它以3种不同的方式进行处理,下面的伪代码对此进行了说明。 动态SQL,它包含一个字符串并执行它。 使用用户定义的函数将定界字符串拆分为表 使用XML作为参数而不是带分隔

  • 我需要解析“txf”格式的数据文件。这些文件可能包含 1000 多个条目。由于格式像JSON一样定义得很好,我想做一个像JSON这样的通用解析器,它可以序列化和解串化txf文件。 与JSON相反,标记没有办法识别对象或数组。如果一个带有相同标签的条目出现,我们需要把它看作一个数组。 标记对象的开始。 标记对象的成员 标记对象的结尾 下面是一个示例“txf”文件 我能够使用NSScanner创建通用

  • 问题内容: 这是我的代码: 导致此错误: 可捕获的致命错误:传递给phpwtf()的参数1必须是字符串的一个实例,字符串给定 看到PHP能够一口气识别并拒绝所需的类型,这不只是一点点Orwellian。 该死的,有五个灯。 PHP中字符串的类型提示等效于什么?对答案的额外考虑,可以准确解释这里发生的情况。 问题答案: 在PHP7之前,类型提示只能用于强制对象和数组的类型。标量类型不是类型隐含的。在

  • 问题内容: 字符串池是什么意思?以下声明之间有什么区别: JVM存储这两个字符串之间有什么区别吗? 问题答案: 字符串池是JVM对字符串实习概念的特定实现: 在计算机科学中,字符串保留是一种仅存储每个不同的字符串值的一个副本的方法,该值必须是不变的。内部字符串使某些字符串处理任务更加节省时间或空间,但代价是在创建或嵌入字符串时需要更多时间。不同的值存储在字符串内部存储池中。 基本上,字符串内部存储

  • 问题内容: 因此,最近我一直在制作python脚本,用于从大型文本文件(> 1 GB)中提取数据。问题基本上可以归结为从文件中选择文本行,然后从某个数组中搜索字符串以查找字符串(此数组中可以包含多达1000个字符串)。这里的问题是我必须找到该字符串的特定出现,并且该字符串在该文件中可能出现无数次。同样,需要一些解码和编码,这另外会降低脚本速度。代码看起来像这样: 我的问题是:有没有办法对此进行优化

  • 18.6. 优化字符串操作 Soundex 算法的最后一步是对短结果补零和截短长结果。最佳的做法是什么? 这是目前在 soundex/stage2/soundex2c.py 中的做法: digits3 = re.sub('9', '', digits2) while len(digits3) < 4: digits3 += "0" return digit