【问题标题】:Performance of list(...).insert(...)list(...).insert(...) 的性能
【发布时间】:2010-11-09 18:25:01
【问题描述】:

我想到了以下有关计算机体系结构的问题。假设我用 Python 做

from bisect import bisect
index = bisect(x, a)      # O(log n)  (also, shouldn't it be a standard list function?)
x.insert(index, a)        # O(1) + memcpy()

这需要log n,另外,如果我理解正确的话,还有x[index:] 的内存复制操作。现在我最近读到瓶颈通常在于处理器和内存之间的通信,因此内存复制可以由 RAM 非常快地完成。是这样的吗?

【问题讨论】:

    标签: python architecture memory list memcpy


    【解决方案1】:

    Python 是一门语言。 Multiple implementations exist,它们可能对列表有不同的实现。因此,如果不查看实际实现的代码,您无法确定列表是如何实现的以及它们在特定情况下的行为方式。

    我敢打赌,对列表中对象的引用存储在连续的内存中(当然不是作为链表......)。如果确实如此,那么使用x.insert 插入将导致被插入元素后面的所有元素都被移动。这可以通过硬件有效地完成,但复杂度仍然是O(n)

    对于小型列表,bisect 操作可能比x.insert 花费更多时间,即使前者是 O(log n) 而后者是 O(n)时间>。然而,对于长列表,我会冒险猜测x.insert 是瓶颈。在这种情况下,您必须考虑使用不同的数据结构。

    【讨论】:

    • 好吧,我不是说 memcpy() 是 O(1) - 我知道它是 O(n),但常数可能很小 - 我不确定它是否真的通过内存优化。但是,如果它被优化为比您天真地想象的快 1000 倍,那可能是值得了解的。
    • 在某些情况下,列表中可能没有任何剩余空间,因此必须在分配新的空闲内存后复制整个列表,而不仅仅是一个 memmove/memcpy。
    • 答案是有效的,但第一段通常不一定正确。一种语言可以指定在某些情况下哪些操作被设计为高效,因此即使不查看特定实现的源代码,您也可以确信这些操作的某些性能属性,假设实现是一致的。
    【解决方案2】:

    如果您需要具有更好插入性能的列表,请使用 blist module

    【讨论】:

      【解决方案3】:

      CPython 列表是连续的数组。 O(log n) 对分和 O(n) 插入中的哪一个支配您的性能配置文件取决于列表的大小以及 O() 中的常量因素。特别是,由 bisect 调用的比较函数可能会很昂贵,具体取决于列表中对象的类型。

      如果您需要保存可能较大的可变排序序列,那么 Python 列表类型底层的线性数组不是一个好的选择。根据您的要求,堆​​、树或跳过列表可能是合适的。

      【讨论】:

        猜你喜欢
        • 2014-12-28
        • 2014-05-26
        • 1970-01-01
        • 2012-05-09
        • 2019-07-22
        • 1970-01-01
        • 2015-04-10
        • 1970-01-01
        • 2010-12-15
        相关资源
        最近更新 更多