我最近解决了一个年龄段我的待办事项清单底部附近的编码问题。我什至设法将递归功能放入解决方案中。是的,计算机科学!我走开了,感觉自己做得好……但是感觉不对。我怀疑我对使用凉爽技术的兴奋导致了效率较低的算法。我需要进行一些实验,看看我的怀疑是否正确。
当我使用deno时,我决定使用内置的benchmarker tool,并开始编码我的测试套件。
参考函数
我写的递归函数是以the Lodash get method的方式解决并返回嵌套对象树中的位置。我想减少对外部功能的依赖性,因此决定不仅进口,而且事实证明这一点很容易:
export function arrayWise(ref, context) {
const [key, ...rest] = ref.split(".");
if (rest.length) {
return arrayWise(rest.join("."), context[key]);
}
return context[key];
}
到目前为止,一切都很好。抓住引用,分为.
字符上的数组,检查是否仍有级别要下降,如果是的,则将其下降到它们中,传递缩短的键并重置上下文以降低较低级别。如果没有,请从上下文返回查找值。此回报将通过呼叫堆栈冒出来,并作为原始呼叫的结果显示。
第一个替代方案
重复打开并加入ref
变量使我感到困扰。这是一个操作的效率?
有了一些轻巧的修补,我想出了这种选择,这在分裂绳子方面有些外科手术:
export function stringWise(ref, context) {
const cutMark = ref.indexOf(".");
if (cutMark === -1) return context[ref];
const key = ref.slice(0, cutMark);
const rest = ref.slice(cutMark + 1);
return stringWise(rest, context[key];
}
这次,我们在字符串中找到了第一个.
的位置。如果没有,只需返回对象引用的数据即可。如果不是,则使用slice
方法确定key
和rest
。然后返回调用下一层的结果。
写基准
让我们看看这是如何在性能方面堆叠的。
编写基准测试很轻松!我省略了导入并为简洁设置测试context
对象。请注意,arrayWise
函数被设置为基准情况。
Deno.bench({
name: "arrayWise",
baseline: true,
fn: () => { arrayWise("look.me.up", context)
});
Deno.bench({
name: "stringWise",
fn: () => { stringWise("look.me.up", context) }
});
这保存在名为lookup.bench.ts
的文件中。当您运行deno bench
时,命令提示符搅拌片刻,然后产生这样的输出:
我的直觉是正确的。我的算法速度提高了三倍。那绝对是“第一,最糟糕的”尝试。
第二个选择
在这一点上,我开始对递归感到不安。称全新的功能感觉就像是一件相当沉重的事情。将其写成循环会更快吗?回到代码编辑器,我想到了:
export function nonRecursive(ref, context) {
let result = context;
for (const key of ref.split(".")) {
result = result[key];
}
return result;
}
这次,我在此功能中的参考中循环浏览键,每次都重新分配结果。当我处于循环结束时,我已经得到了答案,所以我退还了。
我将新的基准测试添加到文件中,然后再次运行deno bench
。这是这次的结果:
另一个加速,几乎是第一个优化的速度的两倍。事实证明,递归,虽然很酷的技术有时是过度杀伤,并且通常在线性处理(例如此示例)的情况下最好避免。
。最后的选择
我以为我会看到我将典型(非常简洁的)JavaScript写作样式放在板凳上时发生了什么。
export const extremeGilesMode = (ref, context) =>
ref.split(".")
.reduce(
(result, key) => result[key],
context
);
单线!我将是第一个承认的人,这比上一个示例要少得多。它将ref
字符串分成组成部分,然后在结果数组上调用koude12 array method。这是在数组上迭代的,每次都会从上一个result
返回key
属性。 reduce
的第二个参数设置了result
的初始值。在这种情况下,我们将其设置为context
。这是与nonRecursive
函数几乎相同的技术。
这是简短的,但是它的速度更快吗?
并不是很快,看起来可能会稍慢一些。 nonRecursive
实现是最快的。
我学到了什么?
这是一个内容丰富的实验。在这种情况下,鉴于在纳秒秒中测量了迭代的节省,因此离开初始实施可能会很好。请注意,如果被称为多次,这可能是一个显着的性能放缓。
还值得注意的是,每次运行都会在迭代中给出不同的时间,因此请谨慎引用确定的数字。分析性能时,值得查看百分位数(P75,P99和P995)以检查可变性。
总的来说,我对在DeNo设置基准的容易程度给我留下了深刻的印象。对于那些习惯于基准测试的人有一些局限性,但是作为起动器,它非常强大。
我已经捆绑了examples in a Github repo。通过评论让我知道您对代码的绩效改进。