【问题标题】:Caching streams in Functional Reactive Programming在函数式反应式编程中缓存流
【发布时间】:2015-07-22 00:21:15
【问题描述】:

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

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

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

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

由于在每次需要解析 YAML 时都会调用此函数,因此会降低性能并根据分析占用超过 50% 的 CPU 时间。

作为修复,我使用存储为缓存流的实例变量的 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;
}

这可能是解决问题的好方法,但我觉得不对。我想知道是否有人能想到一个更简洁的解决方案,它可能使用更实用的方法(闭包等),甚至可以像缓存函数一样添加到流中的扩展。

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

我喜欢任何能给我带来全新视角的想法!

【问题讨论】:

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


    【解决方案1】:

    嗯,我认为一个答案是抽象出缓存like so

    class Test {
        static function main() {
            var sideeffects = 0;
            var cached = memoize(function (x) return x + sideeffects++);
            cached(1);
            trace(sideeffects);//1
            cached(1);
            trace(sideeffects);//1
            cached(3);
            trace(sideeffects);//2
            cached(3);
            trace(sideeffects);//2
        }
        @:generic static function memoize<In, Out>(f:In->Out):In->Out {
            var m = new Map<In, Out>();
            return
                function (input:In) 
                    return switch m[input] {
                        case null: m[input] = f(input);
                        case output: output;
                    }
        }
    }
    

    您可能会在未来为memoize 找到更“实用”的实现。但重要的是它现在是一个单独的东西,你可以随意使用它。

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

    【讨论】:

    • 这样更简洁,是朝着正确方向迈出的一步。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2013-07-24
    • 2010-11-04
    • 2010-11-06
    • 2019-12-27
    • 2014-05-09
    • 2019-10-25
    • 2013-11-20
    • 2015-08-21
    相关资源
    最近更新 更多