4、串行程序并行化
考虑这样一个问题:统计某个工程的代码行数。首先想到的思路便是,递归文件树,每层递归里,循环遍历父文件夹下的所有子文件,如果子文件是文件夹,那么再对这个文件夹进行递归调用。于是问题很轻松的解决了。这个方案可以优化吗?
再回想这个问题,可以发现,循环里的递归调用其实相互之间是独立的,互不干扰,各自统计自己路径下的代码文件的行数。于是,发现了这个方案的可优化点——利用线程池进行并行处理。于是一个串行的求解方案被改进成了并行方案。
不能光说不练,写了一个Demo,对串行方案和并行方案进行了量化对比。代码如下:
[java]viewplaincopyimportjava.io.*;
importjava.util.Queue;
importjava.util.concurrent.*;
/**
*Createdbycdlvshengon2016/5/16.
*/
publicclassParallelSequentialContrast{
intcoreSize=Runtime.getRuntime().availableProcessors();
ThreadPoolExecutorexec=newThreadPoolExecutor(coreSize*4,coreSize*5,0,TimeUnit.SECONDS,
newLinkedBlockingQueue《Runnable》(10000),newThreadPoolExecutor.CallerRunsPolicy());
Queue《Future《Integer》》queue=newConcurrentLinkedQueue《Future《Integer》》();
privateintcountLineNum(Filef){
if(!f.getName().endsWith(“java”)&&!f.getName().endsWith(“.js”)&&!f.getName().endsWith(“.vm”))return0;
intsum=0;
try{
BufferedReaderbr=newBufferedReader(newFileReader(f));
Stringstr=null;
while((str=br.readLine())!=null)sum++;
}catch(FileNotFoundExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}
returnsum;
}
privateclassTaskimplementsCallable《Integer》{
Filef;
publicTask(Filef){
this.f=f;
}
publicIntegercall()throwsException{
intsum=0;
if(f.isDirectory()){
File[]fs=f.listFiles();
for(Filefile:fs){
if(file.isDirectory())queue.add(exec.submit(newTask(file)));
elsesum+=countLineNum(file);
}
}elsesum+=countLineNum(f);
returnsum;
}
}
publicintparallelTraverse(Filef){
queue.add(exec.submit(newTask(f)));
intsum=0;
while(!queue.isEmpty()){
try{
Future《Integer》future=queue.poll();
sum+=future.get();
}catch(InterruptedExceptione){
e.printStackTrace();
}catch(ExecutionExceptione){
e.printStackTrace();
}
}
exec.shutdown();
returnsum;
}
publicintsequentialTraverse(Filef){
intsum=0;
if(f.isDirectory()){
File[]fs=f.listFiles();
for(Filefile:fs){
if(file.isDirectory())sum+=sequentialTraverse(file);
elsesum+=countLineNum(file);
}
}elsesum+=countLineNum(f);
returnsum;
}
publicvoidparallelTest(ParallelSequentialContrastpsc,Stringpathname){
longstart=System.currentTimeMillis();
intsum=psc.parallelTraverse(newFile(pathname));
longduration=System.currentTimeMillis()-start;
System.out.println(String.format(“paralleltest,%dlinesofcodewerefound,timecostis%dms”,sum,duration));
}
publicvoidsequentialTest(ParallelSequentialContrastpsc,Stringpathname){
longstart=System.currentTimeMillis();
intsum=psc.sequentialTraverse(newFile(pathname));
longduration=System.currentTimeMillis()-start;
System.out.println(String.format(“sequentialtest,%dlinesofcodewerefound,timecostis%dms”,sum,duration));
}
publicstaticvoidmain(String[]args){
ParallelSequentialContrastpsc=newParallelSequentialContrast();
Stringpathname=“D:\\Code_Git”;
psc.sequentialTest(psc,pathname);
psc.parallelTest(psc,pathname);
}
}
因为要不断的扫磁盘(虽然我的是固态硬盘),所以并行方案的线程池开的很大。IO密集型程序的相对CPU密集型程序的线程池会更大。
程序运行结果如下:
[plain]viewplaincopysequentialtest,415079linesofcodewerefound,timecostis364ms
paralleltest,415079linesofcodewerefound,timecostis163ms
可以发现,在结果同等精确的情况下,串行方案耗时是并行方案的两倍多。这个是在我个人PC上做的测试,如果是线上服务器运行,恐怕差距只会更加明显。
如果一个大任务,由许多个相互独立的子任务组成,我们就可以在这里找突破点,把一个串行程序并行化,榨干多和服务器的性能!
5、串行和并行的区别
串行通信是指使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息,特别使用于计算机与计算机、计算机与外设之间的远距离通信。终端与其他设备(例如其他终端、计算机和外部设备)通过数据传输进行通信。数据传输可以通过两种方式进行:并行通信和串行通信。在计算机和终端之间的数据传输通常是靠电缆或信道上的电流或电压变化实现的。如果一组数据的各数据位在多条线上同时被传输,这种传输方式称为并行通信。并行通信时数据的各个位同时传送,可以字或字节为单位并行进行。并行通信速度快,但用的通信线多、成本高,故不宜进行远距离通信。计算机或plc各种内部总线就是以并行方式传送数据的。另外,在PLC底板上,各种模块之间通过底板总线交换数据也以并行方式进行。
并行通信传输中有多个数据位,同时在两个设备之间传输。发送设备将这些数据位通过对应的数据线传送给接收设备,还可附加一位数据校验位。接收设备可同时接收到这些数据,不需要做任何变换就可直接使用。并行方式主要用于近距离通信。计算机内的总线结构就是并行通信的例子。这种方法的优点是传输速度快,处理简单。
串行数据传输时,数据是一位一位地在通信线上传输的,先由具有几位总线的计算机内的发送设备,将几位并行数据经并--串转换硬件转换成串行方式,再逐位经传输线到达接收站的设备中,并在接收端将数据从串行方式重新转换成并行方式,以供接收方使用。串行数据传输的速度要比并行传输慢得多,但对于覆盖面极其广阔的公用电话系统来说具有更大的现实意义。
串行数据通信的方向性结构有三种,即单工、半双工和全双工。