如何解决为什么数组结构并不比 Javascript 中的结构数组快得多?
我一直在阅读实体组件系统上下文中的面向数据编程。显然,使用数组结构可以更有效地利用缓存并显着提高性能。基本上,如果您要迭代的所有数据都是连续的,则可以利用缓存位置来大幅提高性能。
因为我将使用 Javascript,所以我想我会首先设计一个小的基准来看看在理想条件下有多少性能提升是可能的。我做得很简单。在第一个测试中,我对遍历结构数组的速度进行了基准测试,在第二个测试中,我对遍历数组结构的速度进行了基准测试。
代码如下:
function randomInt() { return Math.floor(Math.random() * 100) + 1; }
function randomstr() { return Math.random().toString(36).substring(7); }
let samples = 1000;
let count = 10000000;
function benchmarkArrayOfStructs() {
let AOS = [];
for (let i = 0; i < count; i++) {
AOS.push({ health: randomInt(),name: randomstr(),damage: randomInt() });
}
let t1 = performance.Now();
let sum = 0;
for (let x = 0; x < samples; x++) {
for (let i = 0; i < AOS.length; i++) {
let item = AOS[i];
sum += item.health + item.damage;
}
}
console.log(performance.Now() - t1);
}
function benchmarkStructOfArrays() {
let SOA = { health: [],name: [],damage: [] }
for (let i = 0; i < count; i++) {
SOA.health.push(randomInt());
SOA.name.push(randomstr());
SOA.damage.push(randomInt());
}
let t2 = performance.Now();
let sum = 0;
let h = SOA.health;
let d = SOA.damage;
for (let x = 0; x < samples; x++) {
for (let i = 0; i < count; i++) {
sum += h[i] + d[i];
}
}
console.log(performance.Now() - t2);
}
benchmarkArrayOfStructs();
benchmarkStructOfArrays();
有趣的是,后一种解决方案仅比第一种解决方案快 20% 左右。在我看过的各种演讲中,他们声称此类操作的速度提高了 10 倍。此外,直觉上我觉得后一种解决方案应该要快得多,但事实并非如此。现在我开始怀疑这种优化是否值得集成到我的项目中,因为它严重降低了人体工程学。我在基准测试中做错了什么,或者这是实际预期的加速?
解决方法
JavaScript 不会在 JITting 时使用 SIMD 自动矢量化。这是 SoA 布局允许的最大优势之一,但您并没有利用它。 (而且 AFAIK 在 JS 中不容易。)
此外,如果您的代码是在其他情况下空闲的桌面机器上运行的唯一线程,那么您的线程可用的内存带宽比在典型服务器或繁忙的机器上要多得多所有内核都在竞争内存访问。 (Intel Xeons have lower max per-core DRAM memory bandwidth 由于更高的延迟互连,但所有内核都忙时的总带宽更高。假设您错过了私有 L2 缓存。)因此,您的基准测试可能测试了您有大量多余内存带宽的情况。
如果您的对象更大,您可能还会从 SoA 中获得更多好处。您的 AoS 循环正在从每个数组元素中读取 3 个对象中的 2 个,因此只有一小部分数据被“浪费”了。如果您尝试使用更多循环不使用的字段,您可能会看到更大的优势。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。