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

kat*_*'-' 6 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)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我很关心这个reduceRes功能.

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

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

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

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

Yuv*_*kov 4

它怎么能做到这一点呢?我以为只有 1 个核心!

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

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

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

构建.sbt:

"net.java.dev.jna" % "jna" % "4.4.0"
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在让我们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)
}
Run Code Online (Sandbox Code Playgroud)

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

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)
}
Run Code Online (Sandbox Code Playgroud)

并进行两次不同的运行(我正在运行具有 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
Run Code Online (Sandbox Code Playgroud)

第二:

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
Run Code Online (Sandbox Code Playgroud)

在每次执行期间,您会看到执行线程在 3 个不同的核心(0、1 和 3)上获得了执行片段,而我们仍然在单线程环境中运行。这表明,尽管算法的计算绝对是连续的,但这并不意味着您不会看到所有核心都在运行。