【问题标题】:Length of all string in the list: the fastest way列表中所有字符串的长度:最快的方式
【发布时间】:2014-02-01 17:26:34
【问题描述】:

我正在尝试:

python3 -m timeit -c 'len("".join([str(x) for x in range(0, 999999)]))'
10 loops, best of 3: 330 msec per loop

python3 -m timeit -c 'sum((len(y) for y in [str(x) for x in range(0, 999999)]))
10 loops, best of 3: 439 msec per loop

为什么会这样?有更快的方法吗?

附:假设会提前一个字符串列表。

【问题讨论】:

  • 试试sum(map(len, map(str, range(999999)))),它可能会更快。但谁在乎,真的..
  • @wim 我刚刚用预先构建的列表进行了基准测试,您的建议仍然比join + len 慢三倍。
  • 您将许多小字符串连接成一个大字符串,您应该计算每个小字符串的长度,这样会更快。另外,如果你想知道一个数字有多少位,还有更好的方法。
  • 哦,我在 python 2 而不是 python 3 上计时 - 它比两者都快。 编辑: 对我来说,它似乎仍然比 python 3 上的速度更快。奇数
  • sum([len(str(x)) for x in range(999999)]) 怎么样?我猜它会比你的第二个选项快一点。

标签: python performance


【解决方案1】:

暂时忽略那个相当小的时间差,实际上你的两种方式在内存中存在巨大差异

sum((len(y) for y in [str(x) for x in range(0, 999999)]))

这将为每个数字创建一个字符串并将其存储在一个列表中。然后,您使用生成器表达式循环该列表并将长度相加。所以基本上每个数字都有一个字符串,一个存储所有字符串的列表,以及一个被添加到长度的数字。

len(''.join([str(x) for x in range(0, 999999)]))

这将再次为每个数字创建一个字符串并将其存储在一个列表中。然后你创建一个包含所有数字的巨大字符串。然后你调用长度 in (然后是一个 O(1) 调用)。因此,您没有添加的数字(同时对长度求和),但您确实有另一个长字符串,它再次组合了所有其他字符串。

因此,即使这样更快,您也会浪费大量内存,这可能也会影响以后的性能。

要改善这一切,您应该考虑尽可能少地永久创建内容。不要使用列表推导,因为这实际上会创建列表;不要使用str.join,因为这需要一个列表并迭代两次。

sum(len(str(x)) for x in range(0, 999999)))

现在,这仍然会比 len(''.join(…)) 方法慢,但不会有那么多内存开销。实际上,它一次只会创建一个字符串对象,获取它的长度并将其添加到总和中。然后可以立即收集字符串。

这仍然会很慢的原因是 lenstr 都需要在生成器内部的每次迭代中进行查找。要加快速度,请使用map 仅查找两次。 wim 在 cmets 中提出了一个非常好的建议:

sum(map(len, map(str, range(999999))))

对我来说,这实际上比len(''.join(…)) 方式执行得更快。我的计时结果按我的回答中提到的顺序排列:

62.36836282166257
50.54277449168785
58.24419845897603
40.3403849521618

【讨论】:

    【解决方案2】:

    使用 IPython 进行的更好的基准测试表明情况比您想象的要糟糕:

    >>> lst = [str(x) for x in range(0, 999999)]
    >>> %timeit len("".join(lst))
    100 loops, best of 3: 9.94 ms per loop
    >>> %timeit sum(len(x) for x in lst)
    10 loops, best of 3: 62.2 ms per loop
    

    您在这里看到两个影响,Python 中函数调用的开销和迭代的开销。 "".join 两者都没有,因为它是在 C 中执行循环的单个方法调用。可以从 map 获得具有较少内存使用的中等性能:

    >>> %timeit sum(map(len, lst))
    10 loops, best of 3: 29.4 ms per loop
    

    【讨论】:

      【解决方案3】:

      第一个(更快的)版本有 1 次调用 len 函数,1 次调用 join 和 10 万次调用 str。查看第二行,您可以看到 lenstr 都被调用了 100k 次,这使得在第二种情况下的总函数调用次数大约是后者的两倍。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-12
        • 2019-04-28
        • 1970-01-01
        相关资源
        最近更新 更多