函数式编程中的递归不会产生高并发性。这是对的吗?

时间:2014-10-17 17:03:26

标签: functional-programming

我是函数式编程的新手。命令式编程中的循环取代了FP中的递归。另一个声明是FP提供高并发性。这些指令在多核/ cpu系统上并行执行,因为数据是不可变的。

在递归中,由于步骤执行取决于前面的步骤结果,因此步骤不能并行执行。

所以,我假设FP中的递归不会给出高并发性。我对么?

3 个答案:

答案 0 :(得分:1)

排序。您无法获得比数据并行更多的执行并行性;这是Amdahl's law。但是,与典型的顺序算法(无论是功能性还是命令性)相比,您经常拥有更多的数据并行性。考虑例如采用向量的标量倍数:(注意:这是一些用于制作的algol风格的语言):1

function scalar_multiple(scalar c, vector v) {
    vector v1;
    for (int i = 0; i < length(v); i++) {
        v1[i] = c * v[i];
    }
    return v1;
}

显然,这不会并行运行。如果我们使用递归(您可以将其视为Haskell)重新编写函数式语言,情况就不会得到改善:

scalar_multiple c [] = []
scalar_multiple c (x:xn) = c * x : scalar_multiple c xn

这仍然是一个顺序算法!

但是,您可以注意到没有数据依赖性 - 您实际上并不需要先前/后期乘法的结果来计算以后的数据。所以我们有可能在这里进行并行化。这可以用命令式语言来完成:

function scalar_multiple(scalar c, vector v) {
    vector v1;
    parallel_for (int i in 0..length(v)-1) {
        v1[i] = c * v[i];
    }
    return v1;
}

但是这个parallel_for是一个危险的构造。考虑搜索功能:

function first(predicate p, vector v) {
    for (int i = 0; i < length(v); i++) {
        if (p(v[i])) return i;
    }
    return -1;
}

如果我们尝试将for替换为parallel_for来加快速度:

function first(predicate p, vector v) {
    parallel_for (int i in 0..length(v)-1) {
        if (p(v[i])) return i;
    }
    return -1;
}

现在我们不必返回 first 元素的索引来满足条件,只需 元素即可满足条件。我们通过并行化来破坏了函数的合同。

显而易见的解决方案是{0}允许在returnparallel_for。但是还有很多其他危险的结构;事实上,你会注意到我不得不放弃C风格的for循环,因为增量和测试模式本身在并行语言中是危险的。考虑:

function sequence(int n) {
    vector v;
    int c = 0;
    parallel_for (int i = 0..n-1) {
        v[i] = c++;
    }
    return v;
}

这又是一个玩具&#39;示例(&#34;只使用v[i] = i;!&#34;),但它说明了一点:由于并行性,此函数以随机顺序初始化v。事实证明,那些安全的结构是“安全的”。在像parallel_for之类的构造中使用正是纯函数式语言中允许的构造,这使得为这些语言添加并行构造更安全&#39;而不是将它们添加到命令式语言中。

1这只是一个非常简单的例子;当然,真正的并行性涉及找到更大的工作块而不是这个!

答案 1 :(得分:0)

不确定,如果我理解你的话,但通常取决于你想要完成的事情。

单独一个递归不能并行执行其子调用。但是,您可以在同一数据集上进行2次递归。即同时通过两个并发运行的递归函数从左和右处理数组。那些(两个)函数可以(通常)并行运行。

详细说明,只要有一个可以独立运行的函数,你是否有一个递归函数或一个带有循环的函数都没关系。所以关于你的问题:

不,每个定义的递归函数不会给你任何并发性。

答案 2 :(得分:0)

循环由高阶函数替换,而不是直接递归。递归在函数式编程中是一种包罗万象的措施,因为当你的高阶函数不存在时,你需要做什么。

例如,如果要对列表的所有元素运行相同的计算,请使用高度可并行化的map。找到符合特定条件的元素是filter,也是高度可并行化的。

有些算法只是需要前一次迭代的结果才能继续。那些往往需要递归函数的那些,而你是对的,它们通常不容易高度并发。