Scala如何在这里使用我的所有核心?

时间:2017-08-10 10:52:25

标签: scala

object PrefixScan {
  sealed abstract class Tree[A]
  case class Leaf[A](a: A) extends Tree[A]
  case class Node[A](l: Tree[A], r: Tree[A]) extends Tree[A]

  sealed abstract class TreeRes[A] { val res : A }
  case class LeafRes[A](override val res: A) extends TreeRes[A]
  case class NodeRes[A](l : TreeRes[A], override val res: A, r: TreeRes[A]) extends TreeRes[A]

  def reduceRes[A](t: Tree[A], f:(A,A)=>A): TreeRes[A] = t match {
    case Leaf(v) => LeafRes(v)
    case Node(l, r) => {
      val (tL, tR) = (reduceRes(l, f), reduceRes(r, f))
      NodeRes(tL, f(tL.res, tR.res), tR)
    }
  }
}

我关注reduceRes功能。

它有效......计算结果很棒!

但是我去实现了另一个版本reduceResPar,它在前几个分支中使用fork-join来并行化计算。但它没有加速。

然后我回去意识到......上面的版本reduceRes已经在我的机器上使用了所有12个内核!它怎么能这样做?我以为它只是一个核心!

此代码来自Coursera的并行编程课程在第2周的最后一堂课中,我们正在学习并行前缀扫描操作。

1 个答案:

答案 0 :(得分:4)

  

怎么做呢?我以为它只是一个核心!

您看到所有核心的使用并不意味着您的代码执行是并行的。我们可以从实现中看到它的顺序,但是我们不知道每个周期操作系统将在哪个CPU上安排我们的单线程。

当你在线程中执行一个方法时,操作系统会根据它管理的优先级队列决定它将获得多少CPU时间片以及何时获取。

要了解您的算法可能在不同的内核上运行,我们可以询问操作系统当前正在执行我们的线程的逻辑内核。我已经为Windows准备了一个小实现,它有一个名为GetCurrentProcessorNumber()的本机WinAPI方法,它返回我们正在执行的处理器号。我们将JNA用作示例:

build.sbt:

"net.java.dev.jna" % "jna" % "4.4.0"

Java实现:

import com.sun.jna.Library;
import com.sun.jna.Native;

public class ProcessorNumberNative {

    public interface CLibrary extends Library {
        CLibrary INSTANCE = (CLibrary)
                Native.loadLibrary("Kernel32.dll",
                        CLibrary.class);

        Integer GetCurrentProcessorNumber();
    }
}

现在让我们在递归的每个步骤中添加println

def reduceRes[A](t: Tree[A], f: (A, A) => A): TreeRes[A] = t match {
  case Leaf(v) =>
    println(s"Logical Processor Number: ${ProcessorNumberNative.CLibrary.INSTANCE.GetCurrentProcessorNumber()}")
    LeafRes(v)

  case Node(l, r) => 
    println(s"Logical Processor Number: ${ProcessorNumberNative.CLibrary.INSTANCE.GetCurrentProcessorNumber()}")
    val (tL, tR) = (reduceRes(l, f), reduceRes(r, f))
    NodeRes(tL, f(tL.res, tR.res), tR)
}

现在让我们创建一个树并执行:

def main(args: Array[String]): Unit = {

  val tree = Node(Leaf(1),
                Node(Leaf(2),
                     Node(Node(Leaf(24), Leaf(30)),
                          Node(Leaf(3), Node(Leaf(10), Leaf(52))))))

  reduceRes(tree, (a: Int, b: Int) => a + b)
}

并给出这两个不同的运行(我正在运行具有4个逻辑核心的计算机):

首先:

Logical Processor Number: 1
Logical Processor Number: 3
Logical Processor Number: 3
Logical Processor Number: 3
Logical Processor Number: 0
Logical Processor Number: 0
Logical Processor Number: 0
Logical Processor Number: 3
Logical Processor Number: 0
Logical Processor Number: 0
Logical Processor Number: 0
Logical Processor Number: 0
Logical Processor Number: 0

第二

Logical Processor Number: 1
Logical Processor Number: 3
Logical Processor Number: 1
Logical Processor Number: 1
Logical Processor Number: 1
Logical Processor Number: 1
Logical Processor Number: 1
Logical Processor Number: 1
Logical Processor Number: 3
Logical Processor Number: 3
Logical Processor Number: 3
Logical Processor Number: 3
Logical Processor Number: 3

在每次执行期间,您会看到正在执行的线程在3个不同的内核0,1和3上执行了一段执行,而我们仍然在单线程环境中运行。这表明虽然算法的计算肯定是连续的,但这并不意味着你不会看到你所有的核心都在发挥作用。