【问题标题】:Knockout observables and performances淘汰赛观察和表演
【发布时间】:2015-04-03 02:05:17
【问题描述】:

我从事淘汰赛已经 1 年多了,但仍有一个问题我无法解决。 这更像是“语法糖”问题,而不是实际问题。代码简单在 TypeScript 中,但应该没问题,即使你从未听说过。

让我解释一下。

假设我们有一个可观察数组(MyArray),其中每个元素都有“值”可观察。我们想创建一个计算的 observable 来获得所有的总和。

明显的方法是:

public SommeOne = ko.pureComputed((): number => {
    var res = 0;
    for (var i = 0; i < this.MyArray().length; i++) {
        res += this.MyArray()[i].Value();
    }
    return res;
});

但是在这种情况下,对 this.MyArray() 的调用在每次迭代时都会被计算两次。和“价值”一次。这对于小数组(少于 1000 个元素)来说是可以的,但对于更大的数组来说就成了问题。所以,到目前为止我的解决方案是:

public SommeOne = ko.pureComputed((): number => {
    var res = 0;
    var array = this.MyArray();
    for (var i = 0; i < array.length; i++) {
        res += array[i].Value();
    }
    return res;
});

此时我们只评估 Array 函数一次(仍然是 1 次评估 Value,但没关系,我们需要这个)并且它工作正常。

所以最后一个问题:

如何在不创建中间“数组”的情况下实施第二种解决方案?

一个数组很好,但是如果你需要在两个数组之间做减法,或者更复杂的事情,这很快就会失控。

【问题讨论】:

  • 您可以试验Array#reducelodash 中的等效功能:this.MyArray().reduce((res, it) =&gt; res + it.Value(), 0)。但是,请记住,性能优化并不总是直观的,原生 Array#reduce 可能会很昂贵。在进行大量更改之前,分析您的代码以查看慢点在哪里。
  • 嗯,我还没有听说过这个库。在我看来,这是一条可行的路。我对数组的调用将只评估一次。对于简单的情况,它会非常有用,但如果我需要在两个或多个数组之间进行交集或数据收集,它就会失败。至于分析,我正在做,就在今天,将所有 Date.parse 切换为 Date.parseExact,收益很大(大量数据,很多东西)。您可以将此作为答案发布,这将是我的起点。
  • 做出决定后,您可以发布自我回答和 jsPerf 基准测试
  • 所有提供的解决方案都是 O(n),所以我会选择最易读的。 (在我看来,_.reduce)

标签: javascript knockout.js typescript


【解决方案1】:

您几乎可以肯定担心这些优化是在浪费时间。调用this.myArray() 并没有进行任何重要的计算。直接复制knockout source code,调用observable或observable数组时执行的逻辑如下:

function observable() {
    if (arguments.length > 0) {
        // Write
        //[Omitted since not relevant here]
    }
    else {
        // Read
        ko.dependencyDetection.registerDependency(observable);
        return _latestValue;
    }
}

除了函数调用的开销和依赖检测完成的少量工作(当您不在计算中调用时,这可能基本上只是一个noop 函数); observable 函数只返回对数组的引用或它目前恰好持有的任何对象,并且对象引用的成本非常

数组的长度根本不是一个因素。它不会“成为更大数组的问题”,(至少淘汰赛部分不会;算法的其余部分可能取决于您在做什么),并且您对淘汰赛已经缓存的值的缓存肯定不会是主要的性能提升。 (它可能也不会让它变得更糟;虽然我认为它会影响可读性,因为它引入了新变量)

与任何性能问题一样;标准免责声明适用:只有在您首先证明这是需要优化的代码区域,其次才是这个淘汰赛调用是一个重要的性能问题时,您才应该关注这一点。如果这是您的情况,请确保您可以进行一些基准测试以查看缓存该值是否可以提高您的性能,但根据您对问题的表述方式,这里似乎存在更基本的误解。

【讨论】:

  • 最近的这篇博文 (mrale.ph/blog/2014/12/24/array-length-caching.html) 是关于类似的优化;并展示了这些尝试优化的不直观陷阱。
  • 很棒的文章,但正如作者指出的那样:它只是关于 V8;并且个人不太确定 IE & Moz FF JS 引擎是否以相同的效率编译
  • @Tyblitz;当然,但我认为本文的重点不是针对特定案例(array.length缓存与不缓存)提出具体论点,而是作为一般此类优化的警示故事。
  • 我接受这个作为答案,但实际上我大大简化了我的问题。当涉及大量计算(具有多个层)时,该技术会失败。我希望找到一个“神奇”的解决方案。但显然没有。分析后:95% 的时间都花在了敲除库中的“Array.IndexOf”函数上。所以开销并没有你想象的那么小。
  • @Jurion 正如您通过查看我引用的来源所看到的那样,当您从this.MyArray() 阅读(或从写入它,就此而言)时,没有使用indexOf,这是你的问题的主题。您可能遇到了优化问题,但它不是由对 observable 的读写引起的。
猜你喜欢
  • 1970-01-01
  • 2014-11-05
  • 2012-09-08
  • 2012-07-07
  • 2015-01-25
  • 2018-03-30
  • 2015-10-05
  • 2023-03-11
相关资源
最近更新 更多