【问题标题】:Pypy: Why does a "simpler" nested list take longer to flatten?Pypy:为什么“更简单”的嵌套列表需要更长的时间才能展平?
【发布时间】:2012-06-22 23:13:09
【问题描述】:

我需要展平一些嵌套列表。写完 flatten 函数后,我自然是想看看有多少种方法可以破解它。我终于通过 pypy 运行它,很高兴地发现当列表变得非常深时,pypy 的运行速度明显快于 cpython。

但是,我看到了一个奇怪的情况,一个更大、更复杂的列表、更多种类的元素的测试实际上比“更简单”的列表执行得更快。

元素较少的测试 1 的运行速度始终比测试 2 慢大约一秒(使用时间 pypy ./script.py)。

def flatten(lol):
    if not any([isinstance(i, list) for i in lol]):
        return lol

    flatter = [i for i in lol if not isinstance(i, list)]
    for sublist in lol:
        if isinstance(sublist, list):
            flatter.extend(sublist)
    return flatten(flatter)

def silly(l):
    return [l, [l]]

nested_function = [["kaplutis", ["mucus", ["my brain", ["is","filled",["with",["pus",]]]]]], "santa gertrudis",[[[[["innermost",flatten(["in", "func"])]]]]]]

tuples = ["empty tuple retained-->", (), ("2-tuple not flattened",), ("I'm the only thing in this tuple")]

dicts = [{"hip": "hop", "ster": "toad"}]

lone_dict = {"lone": 1, "dict": 2}

silly_list = ["1"]
for i in range(20):
    silly_list = silly(silly_list)

# test 1 - only the absurdly nested list
print(len(flatten(silly_list)))

# test 2 - absurdly nested list, with 
lol = [nested_function, tuples, dicts, lone_dict, silly_list]
print(len(flatten(lol)))

我唯一能想到的是,当 JIT 在第二个测试中处理“silly_list”之前的更简单的嵌套列表时,我遇到了一些意外优化。

【问题讨论】:

  • 澄清:使用 python 2.7.2、python 3.2.2 和 pypy 1.9(来自 pypy-1.9-linux64.tar.bz2 的二进制文件)运行测试,全部在 ubuntu 12.04 x86_64 上运行。
  • 另外:请忽略我的测试用例中的愚蠢值,我倾向于将脑海中出现的任何东西都粘在上面,并且不认为它们可能会分散注意力。 (没有人抱怨过这一点,但一天后看它,我发现它可能会导致人们不那么认真地对待这个问题)。谢谢。
  • 进一步澄清:测试应该单独运行,首先通过cominting out "lol = [nested_function, tuples, dicts, lone_dict, silly_list]" 和 "print(len(flatten(lol)))" ,然后使用 /usr/bin/time 运行,然后在注释掉“print(len(flatten(silly_list)))”后重复测试。这应该首先解释。谢谢。
  • 不要评论你自己的问题,update它。
  • 将不必要的[] 放入any()。运行探查器。循环运行每个测试以排除 jit 预热时间。见Analysing python's performance under PyPy

标签: recursion pypy flatten


【解决方案1】:

这个特殊的 flatten 函数确实在 cpython 和 pypy 中遇到了各种不好的性能点

我们前段时间在 irc 上发现了它,解决方法是创建一个完全不同的 flatten 函数,它在 cpython 中更快,在 pypy 中更快

我不记得网址了,如果 OP 能提供就好了

基本代码是这样的

def flatten(items, akk=None)
 if akk is None:
   akk  = []
 for item in items:
   if isinstance(item, list):
     flatten(item, akk)
   else:
     akk.append(item)

【讨论】:

  • 这确实是代码。很抱歉没有发布更新。过失。我恳求工作量。感谢您的帮助!
【解决方案2】:

包含代码的答案而不是注释

很难理解您的代码的作用。确保它是正确的。后期优化。您可以针对以下内容测试结果:

def flatten(lst, nonscalar_types=list):
    for x in lst:
        if isinstance(x, nonscalar_types):
            yield from flatten(x) # pep 380
        else:
            yield x # non-list type

示例

print(sum(1 for _ in flatten(lol)))

【讨论】:

  • 代码采用嵌套列表(“列表列表”)并返回单个列表中的所有元素。它按预期工作。我确定“确保它是正确的。稍后优化。”总是很好的建议。我确实喜欢在您的示例中对“来自 的字段”的最新使用,但它在 2.7.2 或 pypy 1.9 中无法正常运行,(从 flatten(x) #pep 380 ^ SyntaxError : 无效的语法)。问题仍然是“为什么更大的输入在 pypy 中运行得更快”。
  • @lysdexia:以上是对您的问题的评论,而不是答案(如第一行中明确说明的那样)。 yield from f() 可以替换为 for i in f(): yield i。对于[[1],2,3],您的代码生成[2,3,1] 而不是[1,2,3]
猜你喜欢
  • 2021-07-17
  • 2014-04-21
  • 2019-07-02
  • 1970-01-01
  • 2019-09-19
  • 2015-03-29
  • 2014-12-27
  • 1970-01-01
  • 2017-12-30
相关资源
最近更新 更多