嵌套for循环与Javascript中的Array函数的性能

时间:2014-11-07 23:13:55

标签: javascript node.js performance functional-programming

我昨天写了一小段代码,它使用两个for循环来比较两个数组中的对象(尽管数组是相同的)。

var result = []
for (var i = 0; i < res.length; i++) {
    var tempObj = {}
    tempObj.baseName = res[i].name
    tempObj.cnt = res[i].cnt
    tempObj.matches = []
    for (var j = 0; j < compareArr.length; j++) {
        if(natural.LevenshteinDistance(res[i].name, compareArr[j].name) === options.distance) {
            tempObj.matches.push(compareArr[j])
        }
    }
    if (tempObj.matches.length > 0) {
        result.push(tempObj)
    }
}

然而,在过去的几个月里,我一直在进行函数式编程,并决定使用更具功能性的方法重写代码块,最终得到了这个:

var result = res.
    map(function(baseItem) {
        baseItem.matches = compareArr.
            reduce(function(acc, compItem) {
                if(natural.LevenshteinDistance(baseItem.name, compItem.name) === options.distance) {
                    acc.push(compItem)
                }
                return acc
            }, [])
            return baseItem
        }).
        filter(function(item) {
          return item.matches.length > 0
        })

我的路线觉得响应有点慢,然而,迭代的数据是数据库查询的结果,可能包含数以万计的项目,我想确保我不会受伤服务器的性能无缘无故。所以,我将函数插入到jsperf中,the results令人难过。 for循环以大约2,600 ops / sec运行,而第二个块以大约500 ops / sec运行。 :(

问题是,我的第二块写得不好,可以改进并提速吗?如果没有,这是正常的吗?我看到越来越多的人推动功能风格javascript **。

我是否以风格的名义伤害了表演?我是否应该喜欢学习函数式语言并将其从我的javascript中删除?

** [引用需要amiright?]

http://jhusain.github.io/learnrx/

https://github.com/timoxley/functional-javascript-workshop

https://medium.com/javascript-scene/the-two-pillars-of-javascript-ee6f3281e7f3

John Resig似乎是一个粉丝 - &gt; http://ejohn.org/blog/partial-functions-in-javascript/

http://shop.oreilly.com/product/0636920028857.do

我发现这篇文章非常快速地从非常具体到非常一般,我会编辑范围并在建议时发表新帖。

编辑:为该组添加了lodash和下划线的测试。 Lodash以每秒870次的速度排在第二位,并以每秒475次的速度下调。测试here

我找到了fast.js与for循环和js本机函数here的基准测试,它同样被一个简单的for循环所震撼。

1 个答案:

答案 0 :(得分:0)

数组方法本身比for循环慢,因为1)他们必须在每次迭代时重新构建函数作用域,并且2)其中一些(.map.reduce)必须重建副本数组(所以更多的内存,更多的GarbageCollection和通常更多的操作)。因此,如果您关注速度,请保持尽可能低的速度。

特别是对于您的算法,您可以采取一些措施来改善运行时。您最昂贵的操作是LevenshteinDistance,因此进行了优化,可以显着提高速度。

你能做的最简单的事情是对字符串进行长度检查并提前返回:你知道如果2个字符串的长度相差超过options.distance,那么它们的Levenshtein距离至少会更大那么,你可以很容易地早日回归:

for (var j = 0; j < compareArr.length; j++) {
    // This check was added
    if (Math.abs(res[i].name.length - compareArr[j].name.length) > options.distance) {
        continue;
    }
    if(natural.LevenshteinDistance(res[i].name, compareArr[j].name) === options.distance) {
        tempObj.matches.push(compareArr[j])
    }
}

该方法本身也有一些改进,在另一个stackoverflow帖子中有更好的解释:https://stackoverflow.com/a/3183199/574576