【发布时间】:2020-07-15 18:41:05
【问题描述】:
我注意到我发现的所有深度相等实现都使用递归,理论上迭代形式应该更快。但是,它对我来说有点慢,我不明白为什么。
假设数据是JSON.parse 的结果(即基元、普通对象和数组)。
递归:
function equals1(x, y) {
if (x === y) return true;
if (Array.isArray(x) && Array.isArray(y)) {
if (x.length !== y.length) return false;
for (let i = 0; i < x.length; i++) {
if (!equals1(x[i], y[i])) return false;
}
return true;
}
if ((typeof x !== 'object') || (typeof y !== 'object')) return false;
const xKeys = Object.keys(x);
const yKeys = Object.keys(y);
if (xKeys.length !== yKeys.length) return false;
for (const k of xKeys) {
if (!y.hasOwnProperty(k)) return false;
if (!equals1(x[k], y[k])) return false;
}
return true;
}
迭代:
function equals2(a, b) {
const stack = [a, b];
let idx = 2;
while (idx > 0) {
const x = stack[idx - 1];
const y = stack[idx - 2];
idx -= 2;
if (x === y) continue;
if (Array.isArray(x) && Array.isArray(y)) {
if (x.length !== y.length) return false;
for (let i = 0; i < x.length; i++) {
idx += 2;
if (idx > stack.length) stack.push(x[i], y[i]);
else {
stack[idx - 1] = x[i];
stack[idx - 2] = y[i];
}
}
} else {
if ((typeof x !== 'object') || (typeof y !== 'object')) return false;
const xKeys = Object.keys(x);
const yKeys = Object.keys(y);
if (xKeys.length !== yKeys.length) return false;
for (const k of xKeys) {
if (!y.hasOwnProperty(k)) return false;
idx += 2;
if (idx > stack.length) stack.push(x[k], y[k]);
else {
stack[idx - 1] = x[k];
stack[idx - 2] = y[k];
}
}
}
}
return true;
}
我使用索引而不是传统的stack.pop 方法,因为它稍微快一些。
JSPerf:https://jsperf.com/deep-object-compare-123/1
数据来自Reddit:https://www.reddit.com/r/javascript.json
对我来说,迭代版本在 Chrome 和 Edge 上慢 20-25%,在 Firefox 上速度相同。我尝试预先分配堆栈数组并删除continue,但它并没有改变结果。据我所知,JS引擎可以优化尾递归函数,但这不是尾递归。
有什么想法吗?
【问题讨论】:
-
theoretically the iterative form should be faster.... 你是从哪里得到这个概念的? ...迭代算法总是与递归算法不同,因此哪个更快取决于算法。不涉及“理论”假设。 -
你在几秒钟内完成了更多额外的工作。
-
只是好奇为什么你认为迭代会更快。从代码复杂度的差异来看,迭代版本有更多的分支和内存操作。我不知道在典型的 JS 引擎中调用堆栈有多昂贵,但考虑到最终在 CPU 级别发生的基本操作,您的测试结果对我来说很有意义。
-
我认为创建堆栈帧应该比查找数组索引更昂贵
-
我不认为在 JS 中操作数组比在底层 C 实现中操作堆栈帧更快。
标签: javascript algorithm performance recursion