当前位置: 首页 > 面试题库 >

为什么开始比切片慢

公羊曜灿
2023-03-14
问题内容

为什么执行startwith速度比切片慢?

In [1]: x = 'foobar'

In [2]: y = 'foo'

In [3]: %timeit x.startswith(y)
1000000 loops, best of 3: 321 ns per loop

In [4]: %timeit x[:3] == y
10000000 loops, best of 3: 164 ns per loop

令人惊讶的是,即使包括长度计算在内,切片仍然明显更快:

In [5]: %timeit x[:len(y)] == y
1000000 loops, best of 3: 251 ns per loop

注意:此行为的第一部分已在
Python的数据分析

(第3章)中进行了说明,但未提供任何解释。

如果有帮助:这是startswith;的C代码。这是输出dis.dis

In [6]: import dis

In [7]: dis_it = lambda x: dis.dis(compile(x, '<none>', 'eval'))

In [8]: dis_it('x[:3]==y')
  1           0 LOAD_NAME                0 (x)
              3 LOAD_CONST               0 (3)
              6 SLICE+2             
              7 LOAD_NAME                1 (y)
             10 COMPARE_OP               2 (==)
             13 RETURN_VALUE

In [9]: dis_it('x.startswith(y)')
  1           0 LOAD_NAME                0 (x)
              3 LOAD_ATTR                1 (startswith)
              6 LOAD_NAME                2 (y)
              9 CALL_FUNCTION            1
             12 RETURN_VALUE

问题答案:

__可以通过考虑.操作员完成工作所需的时间来解释 某些 性能差异:

>>> x = 'foobar'
>>> y = 'foo'
>>> sw = x.startswith
>>> %timeit x.startswith(y)
1000000 loops, best of 3: 316 ns per loop
>>> %timeit sw(y)
1000000 loops, best of 3: 267 ns per loop
>>> %timeit x[:3] == y
10000000 loops, best of 3: 151 ns per loop

差的另一部分可以通过以下事实来解释startswith是一个 函数 ,和甚至无操作html" target="_blank">函数调用需要一点时间:

>>> def f():
...     pass
... 
>>> %timeit f()
10000000 loops, best of 3: 105 ns per loop

这不能 完全 解释差异,因为使用切片的版本会len调用函数,并且速度仍然更快(与sw(y)267 ns相比):

>>> %timeit x[:len(y)] == y
1000000 loops, best of 3: 213 ns per loop

我唯一的猜测是,Python可能会针对内置函数优化查找时间,或者对len调用进行了大幅优化(这可能是事实)。可以使用自定义len功能进行测试。也许这是LastCoder识别出的差异所在。

startswith比切片更复杂…

2924 result = _string_tailmatch(self,
2925 PyTuple_GET_ITEM(subobj, i),
2926 start, end, -1);

在干草堆开始时,这不是一个简单的字符比较循环。我们正在寻找一个遍历向量/元组(subobj)并_string_tailmatch在其上调用另一个函数()的for循环。多个函数调用在堆栈,参数完整性检查等方面都有开销。

startswith 是库函数,而切片似乎是内置在语言中的。

2919 if (!stringlib_parse_args_finds("startswith", args, &subobj, &start, &end))
2920 return NULL;


 类似资料:
  • 问题内容: 我很好奇拆包切片并将其作为参数发送给可变参数函数。 假设我们有一个带有可变参数的函数: 如果我们不想传入一个接口,它就可以工作,那么我们是否拆包都没关系: 如果我们有一片片的话,那会很棘手。在这里,编译器不允许我们传递解压版本: 错误提示: 在解包参数中不能将sliceOfSlices(类型[] [] interface {})用作类型[] interface {} 我不知道为什么会这

  • 问题内容: 考虑下面的go代码: : 我不明白的是,为什么taste_fruits的容量为3,直觉上我希望为2,因为这是切片的长度? 而且,如果tasty_fruits的容量为3,那么为什么: 造成: 问题答案: 这行: 创建一个 数组 ,而不是一个切片。即使您仅提供了3个元素,它也有4个元素。输出: 切片: 结果是: 长度:明显2.容量? 的 容量 是…的片的长度和超过所述切片中的[基本]阵列的

  • 有人在IRC中提到它是切片问题。

  • 问题内容: 许多Java书籍都将该语句描述为比该语句快。但是我没有找到任何地方为什么切换比if更快。 例 我有一种情况,我必须选择两项中的任何一项。我可以使用 要么 考虑item和BREAD是一个恒定的int值。 在上面的示例中,哪个操作更快,为什么? 问题答案: 因为有很多情况下,有一些特殊的字节码可以有效地评估switch语句。 如果使用IF语句实现,则将进行检查,跳转到下一个子句,进行检查,

  • 问题内容: 为什么会发生以下现象: 可执行示例 问题答案: 构建一个空切片,就像一个空数组一样,它是一个有效且有用的对象(在所有语言中,而不仅仅是在Go中)。 空切片还仍然指向基础数组,位置和容量,有时可以扩展: 另一方面,长度为负的切片不一致。它没有任何意义,因此被禁止。

  • 问题内容: 我想知道为什么你不能做: 我发现这将需要运行时在片上执行循环以转换每个元素,这将是非惯用的Go。这很有道理。 然而,这可能不会被刚刚走样编译器解决的,所以在内部它们是相同的,他们使用相同类型的头底下?我猜答案虽然不是我好奇为什么。 问题答案: 这个: 是类型转换。根据规范,转换具有特定的规则: 在以下任何一种情况下,可以将非恒定值转换为类型: 是分配给。 的类型,并且具有相同的基础类型