【问题标题】:initialise javascript array with zeros用零初始化javascript数组
【发布时间】:2012-06-09 18:32:20
【问题描述】:

我正在测试用零初始化大型 javascript 数组的不同方法。到目前为止,带有 push(0) 的简单 for 循环似乎远远优于其他方法(请参阅 http://jsperf.com/initialise-array-with-zeros),但我对这个测试的有效性存有疑问。

在实践中,您只会创建一次如此大的数组并将其缓存,以便稍后当您再次需要大型初始化数组时,您可以简单地对其进行切片。因此,我认为最重要的评估是第一次执行此代码所花费的时间,而不是多次试验的平均值。

有人不同意吗?或者有人知道我如何/在哪里可以测试一轮的时间吗?

编辑:为了回应一些关于分配有这么多零的数组的基本原理的误解,我想澄清两件事。

  1. 不会有稀疏性。我需要创建多个大型数组并将它们用于计算。这些副本将充满浮点数,并且浮点数恰好为零的机会可以忽略不计。
  2. 并非所有计算都在数组上按顺序执行。我相信,与覆盖通过引用传递的数组中的值相比,在该过程中生成数组的函数效率低下(参见例如 gl-matrix.js)。

因此,我的解决方案是创建一个大型的零填充数组,然后在需要新数组时使用 slice(),然后通过引用任何函数来传递该副本以使用它。 Slice 在任何浏览器中都非常快。

现在,尽管您可能仍然担心我为什么要这样做,但我真正感兴趣的是是否有可能在 第一次运行时评估不同初始化方法的性能。我希望有这个时间,因为在我的情况下,我肯定只会运行一次。

是的,我的 jsperf 代码可能遗漏了一些解决方案。因此,如果您有我没有想到的方法,请随时添加!谢谢!

【问题讨论】:

  • 你真的需要一百万个零还是你真的使用了一个默认值为零的稀疏数组?
  • 我不明白为什么人们总是问“为什么”,当问题显然与实际情况有关时,但无论如何,我有非常大的矩阵,因为我使用的三角形有很多顶点。如果通过引用传递结果数组,许多操作会更快。这种方法比分别从每个函数返回一个新创建的数组要快。但是,一百万的确切数字是任意的。可能是 100,000,但话说回来,有什么区别?
  • @Paul 这不是因为我不想让你这样做;当知道更大的上下文时,它通常有助于理解问题的细节。它还可能有助于找到真实问题而不是感知问题的良好解决方案。没有冒犯的意思。
  • 通过引用传递数组与填充数据无关。您仍然可以传递对绝对空或稀疏数组的引用,并将任何缺失值视为 0。您并没有解决您的问题,您正在尝试解决您使用您认为的“解决方案”创建的问题。跨度>
  • @OlegV.Volkov Pointy。感谢您的输入。然而,关于“你试图用你认为的解决方案来解决你所创造的问题”,人们同样可以推断“你不是在回答问题,而是试图用你认为的答案来回答你所创造的问题”。说真的,我在第一次运行时要求进行性能评估,而您提出了与此无关的空数组的内容。无论如何,更新已填充数组中的值比填充空数组要快。只是让你知道。 (抱歉听起来很刺耳。字符太少)

标签: javascript performance optimization


【解决方案1】:

只测试一次操作非常复杂,因为性能会根据计算机正在执行的其他操作而有很大差异。您将不得不多次运行该单个测试,并在每次测试之间重置为相同的条件。 jsperf 多次运行测试的原因是为了得到一个好的平均值来排除异常。

您应该在不同的浏览器中对此进行测试,看看哪种方法总体上是最好的。你会看到你得到了非常不同的结果。

在 Internet Explorer 中,最快的方法实际上不是您测试的两种方法,而是一个分配零的简单循环:

for (var i = 0; i < numzeros; i++) zeros[i] = 0;

【讨论】:

  • 感谢您的解释。我只是想,因为 jsperf 聚合了所有访问者,所以测试第一次运行仍然有意义。显然不是。你提到IE很有趣。一开始我并不想检查,但现在我这样做了,IE8(我只有 8 个要测试)内存不足 :-) 并且总体上很慢。所以很高兴知道我不想在 IE 上运行它。 @jahroy 如果您查看基准测试,您会发现其他方法要快得多。
  • 我可以想象另一种方式:for (var i=0; i&lt;=max_iter;arr[i] = i, i++);
  • @Alexander:你可以做出很多变化,但它基本上是相同的循环。例如,您不需要在单独的语句中进行赋值和递增:for (var i=0; i&lt;=max_iter; arr[i++] = i);
【解决方案2】:

从ES6开始,可以使用filllike:

var totals = [].fill.call({ length: 5 }, 0);

【讨论】:

  • 这不会创建一个数组,它会创建一个普通对象,其中的键是数组的索引。
【解决方案3】:

没有实际的任务可以达到“用零初始化 javascript 数组”,尤其是一个大的。您应该重新考虑为什么需要 0。这是一个稀疏数组,您需要 0 作为默认值吗?然后只需添加一个访问条件,将检索到的值设置为 0,而不是浪费内存和初始化时间。

【讨论】:

  • 答案是内存分配。覆盖数组中的值比向数组中添加值更快(并且与您建议的额外“条件”相比要快得多)。此外,与创建要返回的新数组相比,覆盖现有数组可以节省内存。
  • 我可以确认覆盖值更快:stackoverflow.com/questions/14867295/…。在这个测试中,预填充平均提高了 50% 的性能。相关测试在该 SO 页面中被命名为 test 3 和 4。同样使用普通变量,覆盖现有变量的速度大约快 20%,因此在循环之前设置所有变量是明智的。与预填充数组相比,要获得 500% 的提升,请使用普通变量:variable = 1variable[0] = 1 快​​ 5 倍,即使两者都先填零...
  • ... 所以只有在普通变量不合适时才访问数组,如果你必须多次访问数组成员,请使用普通变量作为临时变量(jsperf.com/read-write-array-vs-variable)。
  • @Timo,正如已经回答的问题,您已经将您的测试和方法联系起来有缺陷。请停下来智取 JS 引擎,让他们的编程工程师为您优化。
猜你喜欢
  • 1970-01-01
  • 2016-12-17
  • 1970-01-01
  • 2015-02-07
  • 2012-09-01
  • 1970-01-01
  • 2015-10-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多