功能反应式编程中的缓存流

时间:2015-07-22 00:21:15

标签: stream functional-programming reactive-programming haxe frp

我有一个完全使用FRP范例编写的应用程序,我认为由于我创建流的方式,我遇到了性能问题。它是用Haxe编写的,但问题不是语言特定的。

例如,我有这个函数返回一个流,该流在每次为特定部分更新配置文件时解析,如下所示:

function getConfigSection(section:String) : Stream<Map<String, String>> {
    return configFileUpdated()
        .then(filterForSectionChanged(section))
        .then(readFile)
        .then(parseYaml);
}

在我使用的反应式编程库promhx中,链的每一步都应该记住它的最后一个解析值,但我想每次调用这个函数时我都会重新创建流并重新处理每一步。这是我使用它而不是库的方式的问题。

由于在每次需要时解析YAML的所有地方都会调用此函数,这会导致性能下降,并且根据性能分析占用CPU时间的50%以上。

作为修复,我使用存储为缓存流的实例变量的Map执行了类似下面的操作:

function getConfigSection(section:String) : Stream<Map<String, String>> {
    var cachedStream = this._streamCache.get(section);
    if (cachedStream != null) {
        return cachedStream;
    }

    var stream = configFileUpdated()
        .filter(sectionFilter(section))
        .then(readFile)
        .then(parseYaml);

    this._streamCache.set(section, stream);
    return stream;
}

这可能是解决问题的一个很好的解决方案,但它对我来说并不合适。我想知道是否有人可以想到一个更干净的解决方案,可能使用更多功能的方法(闭包等),甚至是我可以添加到流中的扩展,如缓存功能。

我能做到的另一种方法是先预先创建流,然后将它们存储在消费者可以访问的字段中。我不喜欢这种方法,因为我不想为每个配置部分创建一个字段,我喜欢能够调用具有特定部分的函数并返回流。

我喜欢任何可以给我一个全新视角的想法!

1 个答案:

答案 0 :(得分:2)

嗯,我想一个答案就是抽象出缓存like so

memoize

您可能会为memoize(parseYaml)找到更具“功能性”的实施方案。但重要的是它现在是一个单独的东西,你可以随意使用它。

您可以选择{{1}},以便在解析文件后,切换文件中的两个状态实际上变得非常便宜。您还可以根据任何证明最有价值的策略来调整memoize以管理缓存大小。