为什么javascript比数组结构更快地处理结构数组?

168*_*807 9 javascript performance

我一直在寻找一种有效的方法来处理javascript中的大型矢量列表.我创建了一套性能测试,使用不同的数据结构执行就地标量向量乘法:

AoS实施:

var vectors = [];
//
var vector;
for (var i = 0, li=vectors.length; i < li; ++i) {
    vector = vectors[i];
    vector.x = 2 * vector.x;
    vector.y = 2 * vector.y;
    vector.z = 2 * vector.z;
}
Run Code Online (Sandbox Code Playgroud)

SoA实施:

var x = new Float32Array(N);
var y = new Float32Array(N);
var z = new Float32Array(N);
for (var i = 0, li=x.length; i < li; ++i) {
    x[i] = 2 * x[i];
    y[i] = 2 * y[i];
    z[i] = 2 * z[i];
}
Run Code Online (Sandbox Code Playgroud)

AoS实施至少快5倍.这令我感到意外.与SoA实现相比,AoS实现每次迭代使用一次索引查找,并且引擎必须在没有保证数据类型的情况下工作.

为什么会这样?这是由于浏览器优化吗?缓存未命中?

另外,在向量列表上执行加法时,SoA仍然稍微高效:

AOS:

var AoS1 = [];
var AoS2 = [];
var AoS3 = [];
//code for populating arrays
for (var i = 0; i < N; ++i) {
    AoS3[i].x = AoS1[i].x + AoS2[i].x;
}
Run Code Online (Sandbox Code Playgroud)

SoA的:

var x1 = new Float32Array(N);
var x2 = new Float32Array(N);
var x3 = new Float32Array(N);
for (var i = 0; i < N; ++i) {
    x3[i] = x1[i] + x2[i];
}
Run Code Online (Sandbox Code Playgroud)

有什么方法可以判断某个操作对于给定的数据结构是否会更高/更低效?

编辑:我没有强调使用类型数组的SoA实现,这就是为什么这种性能行为让我觉得奇怪.尽管有类型数组提供的数据类型保证,但是关联数组的普通数组更快.我还没有看到这个问题的重复.

EDIT2:我发现当声明vector移动到准备代码时不再发生这种行为.当vector声明在for循环旁边时,AoS表面上更快.这对我来说没什么意义,特别是因为引擎应该只是将它固定在示波器的顶部,无论如何.我不打算进一步质疑,因为我怀疑测试框架存在问题.

编辑3:我得到了测试平台开发人员的回复,他们已经确认性能差异是由于外部范围查找造成的.正如预期的那样,SoA仍然是效率最高的.

Tra*_*s J 3

用于基准测试的测试结构似乎相互重叠,导致未定义或不期望的行为。更清晰的测试 ( https://www.measurethat.net/Benchmarks/Show/474/0/soa-vs-aos ) 显示两者之间几乎没有什么区别,并且 SOA 执行速度稍快 (30%)。

然而,就性能而言,这些都对底线无关。这是微观优化的努力。您本质上比较的是 O(n) 到 O(n),其中涉及细微差别。小百分比差异不会产生总体影响,因为 O(n) 被认为是可接受的时间复杂度。

  • @TravisJ“小的百分比差异不会产生总体影响,因为 O(n) 被认为是可接受的时间复杂度。” // 如果这是您代码的瓶颈,那么 30% 的改进并不是微不足道的。在不了解上下文的情况下,你不能只是说“O(n) 是可接受的时间复杂度”。 (7认同)
  • 您链接的基准显示了高达 43% 的改进!我认为您一定混淆了这些数字:AOS 比 SOA 慢 30%。SOA 比 AOS 快 43%。还非常值得注意的是,在实践中,对于大型程序中的大型数组,收益可能会显着更大,因为 SOA 不会用不相关的对象数据破坏 CPU 缓存,同时使 CPU 预取器的工作变得轻松!实际上,根据具体情况,这很可能会将该因素改变一个数量级(理论上在极端情况下甚至会改变两个数量级!) (2认同)