我一直在寻找一种高效的方法来处理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;
}
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];
}
实现AoS至少快了5倍。这让我很惊讶。AoS实现每次迭代比SoA实现多使用一个索引查找,并且引擎必须在没有保证数据类型的情况下工作。
为什么会发生这种情况?这是由于浏览器优化还是缓存未命中?
顺便说一句,当对向量列表执行加法时,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;
}
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];
}
有没有一种方法可以告诉我,对于给定的数据结构,某个操作何时会更有效率/更低效率?
编辑:我未能强调SoA实现中使用了类型化数组,这就是为什么这种性能行为让我感到奇怪。尽管类型化数组提供了数据类型的保证,但普通的关联数组仍然更快。我还没有看到过这个问题的复制。
编辑2:当vector
的声明被移动到准备代码中时,这种行为不再发生。当vector
在for
循环旁边声明时,AoS表现更快。这对我来说没有多少意义,特别是因为引擎应该将其锚定到作用域的顶部。我不会进一步质疑这个问题,因为我怀疑测试框架存在问题。
编辑3:我从测试平台的开发人员那里得到了一个回复,他们确认性能差异是由于外部作用域查找。SoA仍然是最有效的,正如预期的那样。
*= 2
更高效。不知道为什么。 - Veedrac