【问题标题】:Stream reduction identity vs. idempotent value流减少标识与幂等值
【发布时间】:2018-03-23 18:31:50
【问题描述】:

java.util.stream 包文档在归约上下文中给出了 identity 的定义:

identity 值必须是组合器函数的身份。这意味着对于所有ucombiner.apply(identity, u) 等于u

Stream.reduce() 及其原始对应物提供了类似的定义。

据我了解,此定义对于支持并行流是必要的。例如,用于求和的非零种子值可能会乘以并行处理器的数量,从而无法预测地扭曲最终结果。

但是这个定义似乎比支持并行性所必需的更严格。为什么不要求idempotentx,使得combiner.apply(x, x) 等于x?这将保护像 Integer::sum(a, b) -> a * bString::concat 这样的函数不会在多个进程之间发生偏差,同时仍然允许使用任何具有幂等函数的种子,如 Math::max(a, b) -> a | bSets::intersection

我忽略的身份价值有什么独特的好处吗?

【问题讨论】:

    标签: java parallel-processing java-stream reduction idempotent


    【解决方案1】:

    问“为什么不呢?”是不够的。并提供一些不会引起问题的例子。您必须提供证明,通过这种宽松的要求,可以保证顺序和并行评估产生相同的正确结果。

    让我们使用 2 参数 reduce 来简化问题。让我们将自己限制为具有 2 个元素(ab)的流。设身份为i,累加器为op

    按顺序计算时,结果为(i op a) op b。当并行计算时,它可能是 (i op a) op (i op b) (或者甚至可能有一个赤裸裸的 i 介于两者之间,但我们不用担心现在)。我们希望它们都等于 a op b

    如果要求 i 是一个身份,很容易看出顺序和并行评估都是正确的。但是,如果我们对 i 的要求只是 i op i = i,那么它显然不会遵循 (i op a) op (i op b)对于任何关联 op 以及任何 ab 必须等于 a op b

    我能想到的一个反例是空间折叠连接:(x, y) -> (x + y).replaceAll(" +", " ")。它是关联的," " 是幂等的,但不是关于此函数的标识。并行和顺序评估会产生不同的结果。

    【讨论】:

    • 不错的答案,但我不禁想知道是否存在一些适用于我给出的示例的规则。
    • @shmosel 您给出的所有示例都是针对可交换(除了关联)的累加器函数。如果操作是可交换的并且“初始值”是幂等的,那么并行归约将等价于顺序(我认为)。
    【解决方案2】:

    为什么combiner.apply(x, x) == x身份的一个很好的定义?

    假设组合器是max()。当然,max(x, x) == x 是真的。实际上对于任何x 都是如此,这意味着对于max()any 值将是一个好的identity 值。

    这当然不是真的,因为 max() 组合器的唯一有效 identity 值是 MIN_VALUE(或“无值”,例如 nullNaN,假设组合器理解并忽略这样的值)。

    【讨论】:

    • 我并不是在建议 identity 的替代定义。我是说我们不需要如此严格的规则来支持并行减少。换句话说,为什么不允许任何值作为max() 的种子?
    • 因为种子值为例如如果流只包含小于 5 的值,5 不好。它与并行处理无关。要求将identity 值包含在给组合器的一组值中,不会影响结果。例如。只有一个标识值对原始加法是中性的:0。原始乘法只有一个:1.
    • 为什么“不好”?当然会影响结果,但这是意料之中的。
    猜你喜欢
    • 2020-02-16
    • 1970-01-01
    • 1970-01-01
    • 2019-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-15
    • 1970-01-01
    相关资源
    最近更新 更多