为什么 str.strip() 比 str.strip(' ') 快这么多? [英] Why is str.strip() so much faster than str.strip(' ')?

查看:49
本文介绍了为什么 str.strip() 比 str.strip(' ') 快这么多?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用 str.strip.您可以发出不带参数的调用,str.strip(),默认使用空白分隔符,或者自己使用 str.strip(' ')<显式提供参数/代码>.

Splitting on white-space can be done in two ways with str.strip. You can either issue a call with no arguments, str.strip(), which defaults to using a white-space delimiter or explicitly supply the argument yourself with str.strip(' ').

但是,为什么这些功能在定时时表现如此不同?

But, why is it that when timed these functions perform so differently?

使用带有有意数量的空格的示例字符串:

Using a sample string with an intentional amount of white spaces:

s = " " * 100 + 'a' + " " * 100

s.strip()s.strip(' ') 的时间分别是:

The timings for s.strip() and s.strip(' ') are respectively:

%timeit s.strip()
The slowest run took 32.74 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 396 ns per loop

%timeit s.strip(' ')
100000 loops, best of 3: 4.5 µs per loop

strip 需要 396nsstrip(' ') 需要 4.5 μs,类似的场景存在于rsplitlsplit 在相同条件下.此外,bytes objects 似乎也受到影响.

strip takes 396ns while strip(' ') takes 4.5 μs, a similar scenario is present with rsplit and lsplit under the same conditions. Also, bytes objects seem do be affected too.

计时是针对 Python 3.5.2 执行的,在 Python 2.7.1 上的差异较小.关于str.split的文档 没有表明任何有用的东西,所以,为什么会发生这种情况?

The timings were performed for Python 3.5.2, on Python 2.7.1 the difference is less drastic. The docs on str.split don't indicate anything useful, so, why does this happen?

推荐答案

以 tl;dr 的方式:

这是因为两种不同的情况存在两个函数,如unicode_strip;do_strip_PyUnicodeXStrip 第一个执行速度比第二个快得多.

In a tl;dr fashion:

This is because two functions exist for the two different cases, as can be seen in unicode_strip; do_strip and _PyUnicodeXStrip the first executing much faster than the second.

函数do_strip 适用于不存在任何参数且 do_argstrip(包装 _PyUnicode_XStrip)用于以下情况str.strip(arg) 被调用,即提供参数.

Function do_strip is for the common case str.strip() where no arguments exist and do_argstrip (which wraps _PyUnicode_XStrip) for the case where str.strip(arg) is called, i.e arguments are provided.

do_argstrip 只检查分隔符,如果它有效且不等于 None(在这种情况下它调用 do_strip),它调用 _PyUnicode_XStrip.

do_argstrip just checks the separator and if it is valid and not equal to None (in which case it calls do_strip) it calls _PyUnicode_XStrip.

do_strip_PyUnicode_XStrip 都遵循相同的逻辑,使用两个计数器,一个等于 0,另一个等于字符串的长度.

Both do_strip and _PyUnicode_XStrip follow the same logic, two counters are used, one equal to zero and the other equal to the length of the string.

使用两个 while 循环,第一个计数器递增,直到达到不等于分隔符的值,第二个计数器递减,直到满足相同条件.

Using two while loops, the first counter is incremented until a value not equal to the separator is reached and the second counter is decremented until the same condition is met.

区别在于检查当前字符是否不等于分隔符的方式.

The difference lies in the way checking if the current character is not equal to the separator is performed.

在要拆分的字符串中的字符可以用 ascii 表示的最常见情况下,存在额外的小幅性能提升.

In the most common case where the characters in the string to be split can be represented in ascii an additional small performance boost is present.

while (i < len) {
    Py_UCS1 ch = data[i];
    if (!_Py_ascii_whitespace[ch])
        break;
    i++;
}

  • 通过访问底层数组可以快速访问数据中的当前字符:Py_UCS1 ch = data[i];
  • 检查一个字符是否为空格是通过一个简单的数组索引到一个名为 _Py_ascii_whitespace[ch].
  • 所以,简而言之,它非常有效.

    So, in short, it is quite efficient.

    如果字符不在 ascii 范围内,则差异不会那么大,但确实会减慢整体执行速度:

    If the characters are not in the ascii range, the differences aren't that drastic but they do slow the overall execution down:

    while (i < len) {
        Py_UCS4 ch = PyUnicode_READ(kind, data, i);
        if (!Py_UNICODE_ISSPACE(ch))
            break;
        i++;
    }
    

    • 访问是通过Py_UCS4 ch = PyUnicode_READ(kind, data, i);
    • 检查字符是否为空格由Py_UNICODE_ISSPACE(ch) 宏(它只是调用另一个宏:Py_ISSPACE)
    • 在这种情况下,访问底层数据与前一种情况一样,使用 PyUnicode_Read 完成;另一方面,检查字符是否为空格(或实际上,我们提供的任何字符)的检查相对复杂一些.

      For this case, accessing the underlying data is, as it was in the previous case, done with PyUnicode_Read; the check, on the other hand, to see if the character is a white-space (or really, any character we've provided) is reasonably a bit more complex.

      while (i < len) {
           Py_UCS4 ch = PyUnicode_READ(kind, data, i);
           if (!BLOOM(sepmask, ch))
               break;
           if (PyUnicode_FindChar(sepobj, ch, 0, seplen, 1) < 0)
               break;
           i++;
      }
      

      PyUnicode_FindChar使用,尽管效率很高,但与数组访问相比,它要复杂得多,速度也慢得多.对于字符串中的每个字符,它都会被调用以查看该字符是否包含在我们提供的分隔符中.随着字符串长度的增加,不断调用此函数引入的开销也会增加.

      PyUnicode_FindChar is used, which, although efficient, is much more complex and slow compared to an array access. For each character in the string it is called to see if that character is contained in the separator(s) we've provided. As the length of the string increases, so does the overhead introduced by calling this function continuously.

      对于那些感兴趣的人,PyUnicode_FindChar 经过相当多的检查,最终会调用 find_charstringlib 中,其中分隔符的长度为 <10 将循环直到找到字符.

      For those interested, PyUnicode_FindChar after quite some checks, will eventually call find_char inside stringlib which in the case where the length of the separators is < 10 will loop until it finds the character.

      除此之外,请考虑为了到达这里而需要调用的附加函数.

      Apart from this, consider the additional functions that need to already be called in order to get here.

      对于lstriprstrip,情况类似.存在用于执行条带化模式的标志,即:RIGHTSTRIP 用于 rstripLEFTSTRIP 用于 lstrip>BOTHSTRIP 用于 strip.do_strip_PyUnicode_XStrip 内部的逻辑是根据标志有条件地执行的.

      As for lstrip and rstrip, the situation is similar. Flags for which mode of striping to perform exist, namely: RIGHTSTRIP for rstrip, LEFTSTRIP for lstrip and BOTHSTRIP for strip. The logic inside do_strip and _PyUnicode_XStrip is performed conditionally based on the flag.

      这篇关于为什么 str.strip() 比 str.strip(' ') 快这么多?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆