【问题标题】:Performance of nested for loops vs Array functions in JavascriptJavascript中嵌套for循环与数组函数的性能
【发布时间】:2015-01-04 20:35:11
【问题描述】:

我昨天写了一小段代码,它使用两个 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 中排除吗?

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 似乎是个粉丝 -> http://ejohn.org/blog/partial-functions-in-javascript/

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

我意识到这篇文章很快就从非常具体变成了非常笼统,如果有建议,我将编辑范围并发布新帖子。

编辑:为组添加了 lodash 和 underscore 的测试。 Lodash 以大约 870 ops/sec 位居第二,下划线仅 475 ops/sec。测试here

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

【问题讨论】:

  • 内置函数做的太多了,所以速度较慢。通常在实践中这没什么大不了的,但您仍然可以保留抽象,但将它们替换为 fast.js 之类的东西。
  • elclanrs:啊,我应该为 FastJS 和 LoDash 添加测试,我完全忘记了这些选项。 :)
  • 嗯。我为长度添加了缓存,因此每次重复都不会命中,这实际上比不使用缓存要慢,所以对我来说——这些结果是不可靠的。缓存怎么不能比缓存快?顺便说一句 - 对我来说,“firefox” - 本机浏览器功能比库快得多。
  • @jamesemanon:链接到您的缓存测试?我刚刚在 Firefox 中运行了自己的测试,虽然 for 循环仍然是最快的,但本机函数紧随其后,库失败得很惨,lodash 只有 119 ops/sec。当然,这些结果很有趣,但也完全没有意义,因为 node 运行在 v8 引擎上。
  • 和你一样,除了“缓存”版本在“for”之后,但在其他版本之前。请参阅:jsperf.com/forvsfunc/5,我刚刚从您的初始版本创建了一个新版本。

标签: javascript node.js performance functional-programming


【解决方案1】:

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

不过,对于您的算法,您可以采取一些措施来改善运行时间。您最昂贵的操作是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

【讨论】:

    猜你喜欢
    • 2021-10-23
    • 1970-01-01
    • 1970-01-01
    • 2021-03-03
    • 2020-03-25
    • 1970-01-01
    • 1970-01-01
    • 2022-07-06
    • 2012-11-18
    相关资源
    最近更新 更多