Scala:par并行集合计算(fold、aggregate)结果与cpu内核线程的关系

下午在运行以下代码时,结果有两种可能:25或30

val list = (1 to 4).toList
var res = list.par.aggregate(5)(_+_,_+_)

关于aggregate函数,这是一个具有柯里化特征的函数;可以传入自定义两个方法,将计算过程打印一遍

        val list = (1 to 4).toList
        // 第一个()内的5传递给第二个()内seqno函数作为每次循环(列表中元素为n,遍历完整个列表循环则结束)的第一个参数m
        // combine函数将之前的返回值作为传入参数进行运算
        var res = list.par.aggregate(5)(seqno,combine)    
        
        //等同于_+_   
        def seqno(m:Int,n:Int): Int ={
            val s = "seq_exp=%d+%d"
            println(s.format(m,n))
            return m+n
        }

        //等同于_+_
        def combine(m:Int,n:Int): Int ={
            val s = "com_exp=%d+%d"
            println(s.format(m,n))
            return m+n
        }
seq_exp=5+2
seq_exp=5+3
seq_exp=5+1
seq_exp=5+4

com_exp=6+7
com_exp=8+9

com_exp=13+17

最终结果应该是30(5*4+1+2+3+4)才是理想的

这就需要考虑到机器cpu内核线程的数量了,par函数将以上集合转换为4个元素并行集合,也就是理想状态下需要有4个线程参与运算,所以才是4*5,5传递给了给个并行的线程,相当于每个线程运行aggregate()函数,只要在运行全程中有4个线程以上是有运行条件时才会得到理想结果30

即使有足够多的内核进行运行,还要考虑效率问题与cpu占有率,所以为了达到并行的效果(>1线程),至少需要2个线程在不断地运行

我的电脑是4核8线程,所以最理想的状态下有8个线程同时运行(允许8个元素的并行集合),但实际上很难达到

比如增加运行的次数

        val list = (1 to 5).toList
        for (i <- 1 to 10000) {
            var res = list.par.aggregate(5)(_+_,_+_)
            println(res)

        }

理想结果应该为 5*5+5!=40,只有在程序刚跑起来的一会输出了大量的40,而基本后半段结果都为30(5!+3*5)

不过像aggregate和fold应该很少有给第一个函数赋不为0的值的需求,一般都为list.par.aggregate(0)(xx,xx)来对并行集合运算,这样就不需要考虑内核线程影响结果值了,反正是至少有2个线程在运行

fold函数只能做一种运算

val b=Array(1,2,3,4,5,6)
println(b.fold(10)(_+_))  //31
println((b.par.fold(10)(_+_)))   //与内核有关,我最高81,即6个线程在运行,6*10+6!
println((b.par.fold(0)(_+_)))    //21,并行运行结果,效率比b.fold(0)(_+_)高

 


版权声明:本文为qq_39192827原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。