【发布时间】:2021-04-28 11:38:09
【问题描述】:
我一直在阅读有关实体组件系统上下文中的面向数据的编程。显然,使用数组结构可以更有效地利用缓存并显着提高性能。基本上,如果您正在迭代的所有数据都是连续的,则可以利用缓存局部性来大幅提高性能。
因为我将使用 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 引擎会根据代码的使用方式应用不同的优化策略(或根本不应用任何优化策略) .上面的基准非常简单(你会想要做很多数据检索测试,而不仅仅是一个),但这不是主要问题。主要问题是它首先是一个综合基准。
-
我知道一般的基准测试的陷阱,但这似乎很简单,可以解释。您对如何改进基准有什么建议吗?
-
正如我所说,您至少要重复测试(在测试中,而不是单独运行)- 很多。 (例如,10,000 多次。)但是您的项目当前代码是否存在严重的性能问题?
-
我将尝试修改代码示例来做到这一点。也没有当前的代码可以说话。我正在尝试在 Javascript 中为一个项目设计一个最小的实体组件系统,并且我正在使用它来确定使用数组结构与结构数组来实现它是否值得。后者明显更符合人体工程学,所以我更愿意这样做,但如果前者像其他人声称的那样高性能,我可能不得不牺牲人体工程学。我正在尝试确定它是否值得。
-
我更新了代码以考虑样本,但仍然是相同的百分比差异。无论是 1 还是 10,000 个样本,后者都快 20%。
标签: javascript arrays performance benchmarking cpu-cache