【问题标题】:Is array access always constant-time / O(1)?数组访问总是恒定时间/O(1)吗?
【发布时间】:2014-11-05 01:45:05
【问题描述】:

来自 Richard Bird,Pearls of Functional Algorithm Design(2010 年),第 6 页:

对于纯函数式程序员,更新操作需要数组大小的对数时间。公平地说,程序程序员也很欣赏恒定时间的索引和更新 只有当数组很小时才有可能。

数组在什么条件下可以进行非常量访问?这和CPU缓存有关吗?

【问题讨论】:

  • 是的。一次缓存未命中可能会花费数百条机器指令。
  • 你把这句话完全断章取义了。
  • 我同意@JeffMercado。这真的是可变的与不可变的数组/列表/向量(或任何你想称之为的)。
  • 他的引述可能断章取义。如果您忽略他的报价,那么问题仍然存在。

标签: arrays performance optimization


【解决方案1】:

大多数现代机器架构都试图提供单位时间的内存访问。

他们失败了。相反,我们会看到不同速度的缓存层。

问题很简单:光速。如果你需要一个巨大的内存[阵列](极端的,想象一个仙女座星系大小的内存),它会占用巨大的空间,而光不能在短时间内穿过巨大的空间。信息的传播速度不能超过光速。你从一开始就被物理学搞砸了。

因此,您可以做的最好的事情是构建“附近”内存的一部分,其中光只需几分之一纳秒即可遍历(因此是寄存器和 L1 缓存),以及远处的部分内存(磁盘驱动器)。其他实际的复杂情况随之而来,例如电容(想想惯性)会减慢对更远事物的访问速度。

现在,如果您愿意将最远的内存元素的访问时间作为“单位”时间,是的,访问所有内容都需要相同的时间,例如 O(1)。在实际计算中,我们大部分时间都以这种方式处理 RAM 内存,并且我们忽略了其他速度较慢的设备,以避免搞砸我们的简单模型。

然后您会发现对此不满意的人,瞧,您会发现有人针对缓存行访问进行了优化。所以理论上它可能是 O(1),对于小型数组(适合第一级缓存)的行为类似于 O(1),但实际上通常不是。

一个极端的实际情况是一个不适合主内存的数组;现在数组访问可能会导致磁盘分页。

有时即使在这种情况下我们也不在乎。谷歌本质上是一个巨大的缓存。我们倾向于将 Google 搜索视为 O(1)。

【讨论】:

  • 光速在这里没有实际意义。 c = 299,792,458 米/秒 = 11802829071 英寸/秒。对于 3 英寸 RAM 棒,穿过它需要 0.00000000025 秒 = 约 0.25 纳秒。但 RAM 通常具有大约 50 - 150 纳秒的访问时间。所以 0.25 纳秒的光速在这里没有实际意义。
【解决方案2】:

橘子可以是红色的吗?

是的,由于多种原因,它们可能是红色的 -

  • 你把它们涂成红色。
  • 您种植的是转基因品种。
  • 你在火星这个红色星球上种植它们,那里的一切都应该是红色的。
  • 一些实用的(鉴于当今的技术)和不切实际的(虚构/或未来现实)的(理论)清单还在继续......

重点是,我认为您要问的问题实际上是关于两个 正交 概念。即-

Big O Notation - "在数学中,大 O 表示法描述了当参数趋向于特定值或无穷大时函数的限制行为,通常以更简单的函数表示。"

实用性(硬件和软件)一个优秀的软件工程师在架构/设计他们的应用程序和编写代码时应该注意。

换句话说,虽然 Big O Notation 的概念可以称为学术,但它是对算法复杂性(时间/空间)进行分类的最合适的方式......到此为止。 没有必要因为正交问题而搅浑水。

明确地说,我不是说人们不应该了解内部实现细节和事物的工作原理,它们会影响您编写的软件的性能。但是有没有必要将两者混合在一起。例如,说 - 是否有意义 -

数组没有固定时间访问(带有索引),因为 -

  • 大型数组不适合 CPU 缓存,因此会产生高昂的缓存未命中成本。
  • 在内存压力下的系统上,无论大小,阵列都已从物理内存换出到硬盘,不仅会受到缓存未命中的影响,还会受到硬页面错误的影响。
  • 在 CPU 负载极高的系统上,读取假定数组的线程可能会被抢占,并且可能在几秒钟内没有机会执行。
  • 在一个假设的操作系统上,它不仅使用磁盘支持其内存,而且还使用世界另一角的另一台计算机上的额外内存,这将使数组访问速度变得难以想象。

就像我的苹果和橙子的例子一样,当你阅读我越来越荒谬的例子时,希望我试图说明的观点很清楚。

结论 - 任何一天,我都会回答“数组是否具有恒定时间 O(1) 访问(带索引)”这个问题,是的.. 毫无疑问,如果和但是,他们会这样做


编辑:

换一种说法 - 如果 O(1) 不是答案.. 那么 O(log n) 或 O(n log n) 或 O(n^2) 或 O(n ^ 3) 也不是.....当然不是42。

【讨论】:

  • 对不起,但我的脑海中不断涌现出更多的 cmets :) - 等到量子计算机变得商业化 实用且可行.. 那么这门课的所有问题的答案将是一个响亮的肯定......这一切都是在恒定时间内完成的......:D(注意 - 我对量子计算一无所知)
【解决方案3】:

他在谈论计算模型,尤其是基于单词的 RAM 机器

RAM 机器是与实际计算机非常相似的东西的形式化:我们将计算机内存建模为每个 w 位的内存字的大数组,我们可以在 O(1) 时间内读/写任何字

但我们还有一些重要的事情要定义:一个词应该有多大?

我们需要一个字长 w ≥ Ω(log n) 才能至少处理输入的 n 部分。 因此,基于字的 RAM 通常假定字长为 O(log n)

但是让你的机器的字长取决于输入的大小显得奇怪和不现实

如果我们保持字长固定呢?那么即使跟随一个指针也需要 Ω(log n) 时间来读取整个指针

我们需要 Ω(log n) 个字来存储指针和 Ω(log n) 时间来访问输入元素

【讨论】:

  • 我认为你应该补充一点,从这个模型得出的理论 O(n) 实际上与现实没有任何联系:现代计算机在 64 位上的数学运算速度与在 8 位上一样快,并且由于由于 CPU 会根据更长的高速缓存行与 RAM 进行通信,因此所有延迟都完全与大小无关。在现实世界的计算机中,数组索引操作完全独立于数组的大小。
  • 是的,这些模型没有涵盖开发人员必须考虑的复杂性。一些作品是由 Frigo 等人介绍的缓存遗忘结构开始的。 (1999)
  • @cmaster:你从来没有一个足够大的数组来强制你的机器分页吗?
【解决方案4】:

如果一种语言支持稀疏数组,那么对数组的访问就必须通过一个目录,而树结构目录的访问时间是非线性的。或者你的意思是现实条件? ;-)

【讨论】:

    猜你喜欢
    • 2014-05-30
    • 1970-01-01
    • 2010-10-16
    • 1970-01-01
    • 2013-11-04
    • 2017-04-06
    • 2013-04-10
    • 2013-07-25
    • 1970-01-01
    相关资源
    最近更新 更多