【问题标题】:Is flattening JavaScript 2D arrays worth any performance gain?扁平化 JavaScript 2D 数组是否值得任何性能提升?
【发布时间】:2014-02-25 04:33:26
【问题描述】:

据我所知,存储 2D 数据有两种主要方式。一、二维数组:

var array = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
];

// access element at (1, 1)
array[1][1];

另一个,一个带有存储width的平面数组:

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var width = 3;

// access element at (1, 1)
array[1 * width + 1];

互联网上都在说“多维”数组很糟糕,与平面数组和存储宽度相比,性能非常差。 (此外,类型化数组可用于进一步加速第二个变体)。但是,我真的不喜欢使用平面数组访问点所必须使用的语法,并且我认为您应该尽可能接近您的意思。此外,每一帧都有很多(我认为)不必要的数学运算。

我需要快速处理大型 2D 数组(你猜对了,游戏),我想知道展平我的数组是否值得可能的性能提升。那么,是吗?

【问题讨论】:

  • 对其进行基准测试并找出答案。或者,更好的是,不要。如果您正确地封装了数组,那么在游戏结束后,从一种存储方法转换到另一种存储方法几乎不需要花费您任何成本。然后,您实际上将能够生成有用的基准而不是合成基准。
  • @meagar:因为在这种情况下,第二个版本将涉及从一种坐标类型到另一种坐标类型的大量杂耍(而且写起来很痛苦),这会抵消好处吗?这有点难以衡量。
  • 这两个版本都不应涉及除函数调用之外的任何内容,例如 getData(x, y)setData(x, y, value)
  • 为什么要从那里开始优化?我会先从基本重构开始,然后是算法,然后是记忆化,最后,只有当一切都失败时,我才会考虑更改逻辑数据结构以提高性能。
  • 请注意,您可以将宽度存储为数组的属性:array.width = 3;

标签: javascript arrays performance multidimensional-array


【解决方案1】:

由于索引查找时间,一维数组可能具有性能优势。 2D+ 数组首先必须查找一个索引,然后在结果数组中查找另一个索引,依此类推。每次都有初始成本,但总的来说可能是微观的。

如果您真的想要提高性能,那么请考虑使用类型化数组。这些是实际的低级字节数组,您可以将它们与 8 位、16 位、32 位和 32/64 位浮点值一起使用,有符号和无符号(后者不适用于浮点)。

如果您的数字是一种类型,那么您可以使用类型化数组。您可以将它们用作具有索引查找的任何其他数组,但性能可能会提高很多倍,因为它们已由 JS 引擎优化:

var array = new Uint8Array(9);  // 9 x bit-width, here 8 = 9 bytes

// write
array[0] = 1;
array[1] = 2;
...etc.

// read
var value1 = array[0];
...etc.

【讨论】:

  • 我(有点)了解类型化数组,但这些只有在我决定 2D 或 1D 之后才会派上用场。这就是我现在卡住的地方。无论如何,感谢第一段。如果它是微观的,那么我应该使用二维数组。
  • @rvighne 您不能将 2D 与类型化数组一起使用,因为您不能将数组放在索引处,但您可以通过使用 x/y 数学 (pos = widthOf1DArray * y + x) x 作为 1 维中的索引来克服这个问题, y 在第二个。这是一个jsperf,用于显示性能差异。
  • 不错的 jsperf,但是类型化数组与非类型化并不是我真正关心的问题。
  • @rvighne 啊,我误认为它是关于性能的。只是忽略:)
  • @rvighne 感谢您的测试,但测试有一些缺陷:您设置的类型数组错误(需要是字节数或另一个类型的数组缓冲区)并且您使用的是随机( ) 在那里,由于其成本,它可能会扭曲测试。读取的值被转储到窗口对象,并且一维数组的 x/y 数学运算正在执行两个 muls 而不是 1 mul 和 1 add,所有这些都会影响结果。这是一个更新的测试:jsperf.com/2d-array-of-numbers/2
【解决方案2】:

我想我找到了解决自己问题的理想方法!这是benchmark

更新:原始基准测试不正确,更新后的版本显示平面类型数组的性能略好于二维类型数组。

正如大家所警告的那样,二维数组比扁平化版本慢得多。但是,如果第二级是类型化数组,那么它的性能比平面数组更好(即使扁平化的数组也是类型化的)感谢 @Ken 将我指向类型化数组。

这将使我能够按照自己的想法编写代码 (array[x][y]),同时还具有卓越的性能。

【讨论】:

  • 请注意,方法之间的性能差异取决于 JS 引擎,因此也取决于浏览器
【解决方案3】:

我为 1000x1000 数组(比他们的 jsperf 中使用的 OP 更大)创建了一些 jsperf 测试,并添加了返回单个嵌套数组/平面数组切片的方法。

var arrDim = 1000;


var clArr = new Array(arrDim);
for (var i = 0; i < arrDim; i++) {
    clArr[i] = new Uint8Array(arrDim);
}

clArr.getSlice = function(i) {
    return this[i];
}
clArr.setVal = function(i, j, val) {
    this[i][j] = val;
}
clArr.getVal = function(i, j) {
    return this[i][j];
}


var flArr = new Uint8Array(Math.pow(arrDim, 2));

flArr.getSlice = function(i) {
    var start = i*arrDim;
    return this.slice(start, start + arrDim);
}
flArr.setVal = function(i, j, val) {
    this[i*arrDim + j] = val;
}
flArr.getVal = function(i, j) {
    return this[i*arrDim + j];
}

对于获取和设置,Chrome 60 对这两种方法的速度几乎相同,而在 EdgeHTML 15 中,嵌套数组的速度要快约 50%。

但最重要的是,对于获取嵌套数组/切片,嵌套数组要快得多。

【讨论】:

    猜你喜欢
    • 2013-11-22
    • 1970-01-01
    • 2019-12-05
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-08
    • 1970-01-01
    相关资源
    最近更新 更多