编程模型MapReduce由2个过程组成,map和reduce。为什么我们需要映射部分,而我们可以简单地在reduce函数中执行映射。
请考虑以下伪代码:
result = my_list.map(my_mapper).reduce(my_reducer);
这可以缩短为
result = my_list.reduce(lambda x : my_reducer(my_mapper(x)));
第一种方法如何比第二种方法更受欢迎,而第一种方法需要多次传递数据?我的代码示例是否过于简单化?
好吧,如果你参考Hadoop风格的MapReduce,它实际上是map-shuffle-reduce,其中shuffle是map和reduce分开的原因。在更高的一点,您可以考虑数据局部性。每个通过 map 传递的键值对可以生成零个或多个键值对。为了能够减少这些,您必须确保给定键的所有值都可以在单个reduce上使用,因此洗牌。从单个输入对发射的重要对可以由不同的化简器处理。
可以使用映射端聚合或组合器等模式,但归根结底,它仍然是(map(-reduce-shuffle-reduce。
假设数据局部性不是问题,像map和reduce这样的高阶函数提供了一个优雅的抽象层。最后,它是一个声明性 API。像xs.map(f1).reduce(f2)
这样的简单表达只描述什么而不是如何。根据语言或上下文的不同,可以急切或延迟地评估这些操作,可以压缩操作,在更复杂的场景中以许多不同的方式重新排序和优化。
关于你的代码。即使签名是正确的,也不会真正减少传递数据的次数。此外,如果将映射推送到聚合中,则传递给聚合函数的参数不再具有相同的类型。这意味着顺序折叠或更复杂的合并逻辑。
在高层次上,mapreduce就是并行处理。尽管化简器在映射输出上工作,但实际上,每个化简器只会获得部分数据,而这只有在第一种方法中才有可能。
在你的第二种方法中,你的化简器实际上需要映射器的全部输出,这击败了并行性的想法。