问题描述
关于功能定义(而非执行)的性能考虑的本质是什么?什么会导致性能下降,以及在现代js环境中倾向于有效处理哪些情况?
要考虑的一些比较是:
在循环中定义函数还是在循环外定义函数(函数从不使用外部引用):
let cases = {
defineFnInLoop: testArr => {
for (let elem of testArr) {
let fn = elem => elem;
fn(elem);
}
},defineFnBeforeLoop: testArr => {
let fn = elem => elem;
for (let elem of testArr) fn(elem);
}
};
// You can ignore (or scrutinize,if you prefer!) this test code:
(async (batches=50,runs=20*1000,sleepMs=50,constElems=100,elemsPerBatch=100) => {
let timing = Object.fromEntries(Object.entries(cases).map(([ k,fn ]) => [ k,0 ]));
let arr = [ ...new Array(constElems) ].map(v => null);
for (let i = 0; i < batches; i++) {
arr.push(...[ ...new Array(elemsPerBatch) ].map(v => null));
// Interleaved testing of cases helps mitigate effect of cpu fluctuations
for (let k in cases) {
let fn = cases[k];
// Timing sensitive segment; I've done my best to reduce the overhead:
let ms = +new Date(); for (let j = 0; j < runs; j++) fn(arr); timing[k] += new Date() - ms;
}
console.log(`Finished batch ${i + 1} / ${batches} (arr size: ${arr.length})`);
await new Promise(r => setTimeout(r,sleepMs));
}
let [ best,...others ] = Object.entries(timing).sort((v1,v2) => v1[1] - v2[1]);
console.log(`Winner: ${best[0]} (${best[1]}ms)`);
for (let [ name,ms ] of others)
console.log(`Loser: ${name} (${ms}ms; ${(100 * ms / best[1] - 100).toFixed()}% slower)`);
})();
内部或外部定义函数,可能使用外部引用:
let cases = {
useExternalRefInLoop: testArr => {
let ref = null;
for (let elem of testArr) {
let fn = elem => (elem,ref);
fn(elem);
}
},noExternalRefInLoop: testArr => {
let ref = null;
for (let elem of testArr) {
let fn = elem => (elem,elem);
fn(elem);
}
},useExternalRefOutLoop: testArr => {
let ref = null;
let fn = elem => (elem,ref);
for (let elem of testArr) fn(elem);
},noExternalRefOutLoop: testArr => {
let ref = null;
let fn = elem => (elem,elem);
for (let elem of testArr) fn(elem);
}
};
// You can ignore (or scrutinize,ms ] of others)
console.log(`Loser: ${name} (${ms}ms; ${(100 * ms / best[1] - 100).toFixed()}% slower)`);
})();
我已经使用jsbench.me运行了相同的代码,结果相同:
两个比较的结果令人惊讶。对我而言,每次迭代定义一次函数比循环之前一次定义函数要快。另外,引用外部引用(非参数引用)的函数比仅引用其参数的函数看起来更快。当函数使用外部引用并在循环中重复定义时,我发现这尤其令人惊讶-我认为外部引用会降低解释器减轻重复函数定义的影响的能力。 我有信心useExternalRefInLoop
会最慢,但它是第二快,而且只占整体冠军的一小部分!
问题:这些测试定义了小的函数,这妨碍了单独隔离函数定义对性能的影响。我不是100%肯定我的测试代码或测试用例是100%洁净的。最后,我的环境(SO的片段系统,在最新的Edge浏览器中)可能无法在所有环境中都适用,并且一般而言,优化可能会因环境而异。
问题:
- 似乎解释器具有优化同一函数重复定义的巧妙方法,甚至可能 prefer 使用外部引用的函数! 我们应该始终喜欢在循环中定义函数,还是应该更喜欢使用外部引用(仅就性能而言),或者是否存在一些因素会间接阻止这些优化并导致这些策略减少 性能??
- 关于功能定义,其他有关性能的注意事项是什么? (我敢肯定,范围太广了,但是我想感谢任何人想提供的见解!)
注意:不建议过早优化,但并非所有优化都为时过早!
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)