【发布时间】:2014-02-08 17:45:22
【问题描述】:
我在 jre 1.7 中使用 Java fork/join 框架编写了一个多线程程序。该程序旨在在四叉树的所有节点中找到满足指定条件的某些点(四叉树中的每个叶节点可以填充无限数量的点,例如,可以是零或 1000)。我在 16 核处理器机器上测试了多线程程序与串行程序相比的加速比,而加速比仅为 1.3-1.5。下面是伪代码:
public class QuadtreeFindMultiThread extends RecursiveTask<IntArrayList> {
private Quadtree T;
private ObjectArrayList<Node> leaf_nodes;
private ObjectArrayList<Entry> candidatePoints;
private static int POINT_THRESHOLD = 50;
private static int NODE_THRESHOLD = 1;
public QuadtreeFindMultiThread(Quadtree T) {
this.T = T
this.leaf_nodes = T.get_nonempty_leaf_nodes();
this.candidatePoints = new IntArrayList();
}
private QuadtreeFindMultiThread(Quadtree T, IntArrayList leaf_nodes) {
this.T = T;
this.leaf_nodes = leaf_nodes; // reference copy
this.candidatePoints = new IntArrayList();
}
private IntArrayList QuadtreeFind() {
//...
//...
return candidatePoints;
}
private int getPointNum(){
int count = 0;
for(Node node:this.leaf_nodes){
count += node.getAllPoints().size();
}
return count;
}
@Override
public IntArrayList compute() {
if (this.getPointNum() <= POINT_THRESHOLD || this.leaf_nodes.size() <= NODE_THRESHOLD) {// trivial problem, solve by single thread
this.candidatePoints = QuadtreeFind();
} else {// START: divide and conquer
// Divide Step: partition this.leaf_nodes by direction: NW, NE, SW, SE
Partition leaf_nodes to four quadrants: leaf_nodes_NW,
leaf_nodes_NE,
leaf_nodes_SW,
leaf_nodes_SE
// Conquer Step
QuadtreeFindMultiThread thread_NW = new QuadtreeFindMultiThread(
this.T, leaf_nodes_NW);
QuadtreeFindMultiThread thread_NE = new QuadtreeFindMultiThread(
this.T, leaf_nodes_NE);
QuadtreeFindMultiThread thread_SW = new QuadtreeFindMultiThread(
this.T, leaf_nodes_SW);
QuadtreeJoinMultiThread thread_SE = new QuadtreeFindMultiThread(
this.T, leaf_nodes_SE);
// fork three new sub threads
thread_NE.fork();
thread_SW.fork();
thread_SE.fork();
this.candidatePoints.addAll(thread_NW.compute()); // main thread
this.candidatePoints.addAll(thread_NE.join());
this.candidatePoints.addAll(thread_SW.join());
this.candidatePoints.addAll(thread_SE.join());
}// END: divide and conquer
return this.candidatePoints;
}
}
我是 Java 多线程编程的新手,为什么这个程序在 16 核处理器机器上的加速效果如此糟糕?我还在我的笔记本电脑上测试了这个多线程程序,有 2 个内核和 2 个虚拟内核,加速也接近 1.3-1.5。我的笔记本电脑的多线程程序的性能有时甚至比 16 核处理器的机器更好。
看来fork/join framefork的默认调度策略是LIFO,怎么改成FIFO呢?
顺便说一句,我发现处理一些有很多点的叶子节点会占用很多处理时间。我可以修改 fork/join 调度程序,使其首先处理具有大量点的节点吗?因此它应该获得更好的性能。 谢谢!
【问题讨论】:
-
更多节点会增加开销。您希望拥有尽可能少的节点以利用所有 CPU。
-
@PeterLawrey 所以增加 POINT_THRESHOLD 和 NODE_THRESHOLD 来增加线程粒度?但我认为性能不佳可能是由于粒度粗...如果我们增加粒度,那么该线程将处理更多的点,线程的处理时间与叶节点中的点数超线性。因此粗粒度可能会增加处理时间。
-
如果你有 16 个 CPU,你至少需要 16-64 个线程。
标签: java multithreading