【发布时间】:2011-02-22 05:22:58
【问题描述】:
当一个人分割一个任务并让每个部分并行完成时,某些算法的运行时间会显着减少。其中一种算法是归并排序,其中一个列表被分成无限小的部分,然后按排序顺序重新组合。我决定做一个实验来测试我是否可以通过使用多个线程来提高这种排序的速度。我在装有 Windows Vista 的四核戴尔上用 Java 运行以下功能。
一个函数(控制用例)是简单的递归:
// x is an array of N elements in random order
public int[] mergeSort(int[] x) {
if (x.length == 1)
return x;
// Dividing the array in half
int[] a = new int[x.length/2];
int[] b = new int[x.length/2+((x.length%2 == 1)?1:0)];
for(int i = 0; i < x.length/2; i++)
a[i] = x[i];
for(int i = 0; i < x.length/2+((x.length%2 == 1)?1:0); i++)
b[i] = x[i+x.length/2];
// Sending them off to continue being divided
mergeSort(a);
mergeSort(b);
// Recombining the two arrays
int ia = 0, ib = 0, i = 0;
while(ia != a.length || ib != b.length) {
if (ia == a.length) {
x[i] = b[ib];
ib++;
}
else if (ib == b.length) {
x[i] = a[ia];
ia++;
}
else if (a[ia] < b[ib]) {
x[i] = a[ia];
ia++;
}
else {
x[i] = b[ib];
ib++;
}
i++;
}
return x;
}
另一个是在一个扩展线程的类的'run'函数中,每次调用都会递归创建两个新线程:
public class Merger extends Thread
{
int[] x;
boolean finished;
public Merger(int[] x)
{
this.x = x;
}
public void run()
{
if (x.length == 1) {
finished = true;
return;
}
// Divide the array in half
int[] a = new int[x.length/2];
int[] b = new int[x.length/2+((x.length%2 == 1)?1:0)];
for(int i = 0; i < x.length/2; i++)
a[i] = x[i];
for(int i = 0; i < x.length/2+((x.length%2 == 1)?1:0); i++)
b[i] = x[i+x.length/2];
// Begin two threads to continue to divide the array
Merger ma = new Merger(a);
ma.run();
Merger mb = new Merger(b);
mb.run();
// Wait for the two other threads to finish
while(!ma.finished || !mb.finished) ;
// Recombine the two arrays
int ia = 0, ib = 0, i = 0;
while(ia != a.length || ib != b.length) {
if (ia == a.length) {
x[i] = b[ib];
ib++;
}
else if (ib == b.length) {
x[i] = a[ia];
ia++;
}
else if (a[ia] < b[ib]) {
x[i] = a[ia];
ia++;
}
else {
x[i] = b[ib];
ib++;
}
i++;
}
finished = true;
}
}
事实证明,不使用多线程的函数实际上运行得更快。为什么?操作系统和 java 虚拟机是否没有足够有效地“通信”以将不同的线程放在不同的内核上?还是我遗漏了一些明显的东西?
【问题讨论】:
-
只有一个线程时,所有数据都在缓存中。如果在线程之间拆分数据,则需要将部分数据复制到第二个线程的缓存中,然后再次复制回来以进行最终合并。如果比较的成本很小,那么复制的成本可能会更高。您可能会发现使用的线程越多,开销就越大。
-
我没有看到你在任何地方调用 Thread.start()。您的合并看起来很糟糕,因为您只是在一个线程中调用 run() 方法。
-
程序员确实需要摆脱“单线程”的思维模式:这就像八十年代打电话想要他们的单核 80386 CPU 回来一样。我们正在转向一个高度多核的世界,未来最大的性能增强将是由于水平扩展,跨多核。拒绝学习多线程优势的人将生产性能不佳的 API/软件。 OP 可以检查我的答案:正确的多线程算法(包括排序)执行 own 单线程算法。克服它,我们正在走向多核世界。
标签: java multithreading parallel-processing