一个计算示例 现在该测试这一模型了。以下计算示例使用两台计算机并行运行。一台是运行 Windows 98 的 333 MHz Pentium II 计算机,另一台是运行 Windows 2000 专业版的 500 MHz Pentium III 计算机。
为了计算从 1 到 10^9 的所有整数的平方根之和,我创建了 Sqrt 类,它计算 dblStart 和 dblEnd 之间所有整数的平方根之和。
Sqrt 实现 JobCodeInt 接口,因此也实现了 jobCode() 方法。在 jobCode() 方法中,我定义了完成这一计算的代码。
构造函数用于将数据传递给 Sqrt 类,并初始化作业调度器上的所有本地资源。必须将要计算其平方根之和的整数的起止点发送给构造函数。清单 1 是 Sqrt 类的定义
清单 1. 定义 Sqrt 类
//Sqrt 类计算 dblStart 和 dblEnd 之间的所有整数的平方根之和。 //计算在 jobCode() 方法内完成 //该类实现 JobCodeInt 接口,且实现代码位于 jobCode() 方法内 //在构造函数中将数据传递给该类,并初始化作业调度器上的本地资源。 //本例中,要计算其平方根之和的整数序列的起止点被发送给 Sqrt 类 public class Sqrt implements JobCodeInt { double dblStart, dblEnd, dblPartialSum;
public Sqrt(double Start,double End) { dblStart = Start; dblEnd = End; }
public Object jobCode() { dblPartialSum = 0; for(double i=dblStart;i<=dblEnd;i++) //可调用标准的 Java 函数和对象。 dblPartialSum += Math.sqrt(i);
//返回结果,一个标准 Java 类的对象。 return (new Double(dblPartialSum)); } } |
JobDispatcher 类创建 Sqrt 类的两个实例。然后分解主任务,将一项子任务分配给一个 Sqrt 对象(Sqrt1),并将余下的子任务分配给另一个 Sqrt 对象(Sqrt2)。接下来,JobDispatcher 创建 PseudoRemThr 类的两个对象,并将 Sqrt 对象作为参数分别传递给它们。接下来就等待线程执行。
一旦线程执行完毕,就可从每个 PseudoRemThr 实例获得部分结果。将各部分结果组合起来即可得到最终结果,如清单 2 所示。
清单 2. 工作中的 JobDispatcher
//此类可以命名为您选择的任何名称 //这里选用 JobDispatcher 只是为了方便
public class JobDispatcher { public static void main(String args[]) { double fin = 10000000; //代表 10^9 double finByTen = fin/10; //代表 10^8 long nlStartTime = System.currentTimeMillis(); //范围从 1 到 3*10^8 Sqrt sqrt1 = new Sqrt(1,finByTen*3); //范围从 ((3*10^8)+1) 到 10^9 Sqrt sqrt2 = new Sqrt((finByTen*3)+1,fin);
//以下创建 PseudoRemThr 类的两个实例。 //此构造函数的参数如下所示。 //第一个参数:代表子任务的某个类的实例 //第二个参数:执行这一子任务的远程主机 //第三个参数:PseudoRemThr 实例的描述性名称。 PseudoRemThr psr1 = new PseudoRemThr(sqrt1,"//192.168.1.1:3333/","Win98"); PseudoRemThr psr2 = new PseudoRemThr(sqrt2,"//192.168.1.2:3333/","Win2K"); psr1.waitForResult(); //等待执行结束//获取每个线程的结果 Double res1 = (Double)psr1.getResult(); Double res2 = (Double)psr2.getResult(); double finalRes = res1.doubleValue() + res2.doubleValue();
long nlEndTime = System.currentTimeMillis(); System.out.println("Total time taken: " + (nlEndTime-nlStartTime)); System.out.println("Sum: " + finalRes); } } |
性能评价 此计算的总执行时间在 120,000 毫秒到 128,000 毫秒之间。如果在不分解任务的情况下在本地运行同样的任务,执行时间将在 183,241 到 237,641 毫秒之间。
最初,主任务包括计算从 1 到 10^7 的所有整数的平方根之和。为测试性能,我将计算范围扩大到 10^8,最终扩大到 10^9。
随着任务量的增加,远程并行执行和本地执行所需时间的差别也越来越明显。这就是说,当执行大型任务时,远程并行执行消耗的时间较少。远程并行执行并不适合小型任务,因为机器间通信的系统开销不容忽视。随着任务量的增加,机器间通信的开销与在单个机器上执行全部任务的开销相比逐渐变得微不足道。因此,我得出以下结论:伪远程线程系统能很好地完成需要进行大量计算的任务。
使用伪远程线程有哪些优点? 因为伪远程线程是一种基于 Java 的系统,它可以用于实现包含多种操作系统的集群,或异构集群。使用伪远程线程,您就避免了转换原有 C/C++ 代码的麻烦,而且还能利用 Java 标准库及其各种扩展库。此外,伪远程线程使您不必关心内存管理。当然,其缺点就是系统性能与 JRE 性能直接相关。
发展方向
现在相当多的商业应用程序都是用 Java 平台创建的,并考虑到为了利用并行性需要转换原有的 C/C++ 代码的实际困难,现在可能是基于 Java 的超级计算进入商业领域的时候了。在开始创建基于 Java 的应用程序时就将并行性和负载均衡考虑在内是个不错的开端。
互联网就是异构集群的一个很好的例子,因此伪远程线程可以在因特网中部署,将 Web 转换为一个单一的、基于 Java 的超级计算机(要了解这一概念的细节,请参阅参考资源)。然而,从实际应用出发,您应注意到在一个专门执行单一任务的同构集群中将获得最佳结果。
最后,从日常应用出发,伪远程线程使得将局域网(LAN)-- 诸如校园网和家庭网 -- 转换为微型的超级计算机变得相当简单。这就是 Beowulf 系统开创的用法。有了伪远程线程,Java 编程人员也可以创建自己的超级计算机了。
参考资源 "Linux clustering cornucopia"(developerWorks, 2000 年 5 月)为您指点迷津,让您了解当前 Linux 上可用的开放源码集群解决方案和保密源码集群解决方案。
要了解关于分布式操作系统的详细信息,请查看 Andrew S. Tanenbaum 的 Modern Operating Systems(Prentice Hall 出版公司,1992 年 2 月)。
要了解关于并行编程的详细信息,请参阅 Gregory V. Wilson 的 Practical Parallel Programming(麻省理工学院出版社,1995 年 12 月)。
要进一步理解集群,请参阅 Scalable Computing Laboratory 的 Cluster Cookbook。
有关运用 Java 技术和 Web 进行超级计算的深层探讨,请参阅 Laurence Vanhelsuw 的 "Create your own supercomputer with Java?"(JavaWorld, 1997 年 1 月)。
Linux Documentation Project 托管着 Beowulf HOWTO 文档。
访问 Beowulf 网站,以了解 Beowulf 项目的详细信息。
请参阅关于 Oak Ridge 国家实验室著名的 Stone SouperComputer 的详细信息。
Aspen 系统公司是目前提供定制集群解决方案的少数厂商之一。