操作系统实验——串行、多线程和线程池三种方式计算矩阵乘法

来源:互联网 发布:开普勒第一定律知乎 编辑:程序博客网 时间:2024/06/08 14:21

操作系统实验——串行、多线程和线程池三种方式计算矩阵乘法


注:写实验报告的同学在复制代码的时候记得改一改变量名和函数!最好看懂了自己写一下!不要直接copy!太明显了~~

1.实验目的:以多线程(并行)、串行以及线程池三种形式计算矩阵乘法,统计分析三种方法所需要的时间,并根据公式speedup<=1/(s+(1-s)/n),计算出顺序执行占用比s.

2.对公式speedup<=1/(s+(1-s)/n)的解释:speedup=串行时间/并行时间,又称加速比,s是顺序执行占用比,也就是程序中必须串行(单线程)执行的部分,n是你的电脑的内核数,可通过设备管理器查看

3.实现思路:
    1)定义两个较大的1024阶方阵和确定线程数量n(n从2-1024*1024均可以)
    2)实现一个singleThread()函数,用来串行(直接)计算矩阵乘法
    3)实现一个工作线程workThread,每个线程完成矩阵乘法运算工作的1/n,创建n个线程,用多线程计算矩阵乘法
    4)声明一个线程池,将n个线程放入线程池中进行管理,用线程池进行矩阵乘法的运算

4.代码
工作线程类workThread:
package cn.edu.seu.yujun.OS;/** *  * @author Fish * Date:2015/4/7 */public class WorkThread implements Runnable {private int start;//计算开始位置,以此区分工作线程工作任务private int [][]A;private int [][]B;private int [][]C;//工作线程构造方法public WorkThread(int start,int [][]A,int [][]B,int [][]C){this.start=start;this.A=A;this.B=B;this.C=C;}@Overridepublic void run() {int i,j,k;//根据线程数量划分每个工作线程任务for(i=start; i<Driver.M; i +=Driver.NUM_THREADS)      {      for(j=0;j<Driver.N;j++)      {           for( k=0; k< Driver.K;k++)         C[i][j]+=A[i][k]*B[k][j];         }}}}
驱动类Driver:
package cn.edu.seu.yujun.OS;/** * @author Fish * Date:2015/4/7 */import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Driver {public final static int M=1024;//定义常量:矩阵A的行数public final static int K=1024;//定义常量:矩阵A的列数,矩阵B的行数public final static int N=1024;//定义常量:矩阵B的列数final static int NUM_THREADS=2;//定义常量:线程数量private static int [][]A;//矩阵Aprivate static int [][]B;//矩阵Bprivate static int [][]C;//矩阵C//---------------------//驱动类构造方法public Driver(){A=new int[M][K];B=new int[K][N];C=new int[M][N];//A、B、C初始化fillRandom(A);//用0-99的随机数初始化矩阵AfillRandom(B);//用0-99的随机数初始化矩阵Bfor(int i=0;i<M;i++)for(int j=0;j<N;j++)C[i][j]=0;//将C矩阵全置零}//-------------------//初始化方法:产生0-99的随机数初始化矩阵A、Bprivate void fillRandom(int[][] A) {for(int i=0;i<A.length;i++){for(int j=0;j<A[i].length;j++)A[i][j]=(int)(Math.random()*100);}}//--------------------//串行矩阵乘法运算public static void singleThread(){for(int i=0;i<M;i++){for(int j=0;j<N;j++){for(int k=0;k<K;k++)C[i][j]+=A[i][k]*B[k][j];}}}//----------------------//main函数public static void main(String[] args){new Driver();//新建一个驱动类对象//随机产生三组C矩阵位置信息,便于后面验证三种方法计算结果是否都正确int []rol=new int[3];int []col=new int[3];for(int i=0;i<rol.length;i++){rol[i]=(int)(Math.random()*M);col[i]=(int)(Math.random()*N);}//--------------------------------//并行(4线程)方法:建立四个工作线程,每个线程完成矩阵乘法运算的1/4工作        Thread[] workers=new Thread[NUM_THREADS];for(int i=0;i<NUM_THREADS;i++)workers[i]=new Thread(new WorkThread(i,A,B,C));//建立四个工作线程long time1= System.currentTimeMillis();//记录开始时间for(int i=0;i<NUM_THREADS;i++){workers[i].start();//启动四个工作线程}for(int i=0;i<NUM_THREADS;i++){try{workers[i].join();//等待当前线程执行结束}catch(InterruptedException e){e.printStackTrace();}}long time2=System.currentTimeMillis();//记录结束时间//打印方法一使用的时间和矩阵C三个随机位置的值System.out.println("计算["+M+","+K+"]与["+K+","+N+"]阶矩阵乘法,并行("+NUM_THREADS+"线程)用时:"+(time2-time1)+"毫秒");System.out.println(C[rol[0]][col[0]]+" "+C[rol[1]][col[1]]+" "+C[rol[2]][col[2]]);System.out.println();//---------------------------------//方法二:串行也就是直接进行运算for(int i=0;i<M;i++)for(int j=0;j<N;j++)C[i][j]=0;//将C矩阵全置零long time3=System.currentTimeMillis();//记录开始时间singleThread();//调用串行计算函数long time4=System.currentTimeMillis();//记录结束时间//打印方法二使用的时间和矩阵C三个随机位置的值System.out.println("计算["+M+","+K+"]与["+K+","+N+"]阶矩阵乘法,直接计算用时:"+(time4-time3)+"毫秒");System.out.println(C[rol[0]][col[0]]+" "+C[rol[1]][col[1]]+" "+C[rol[2]][col[2]]);System.out.println();//------------------------------//方法三:使用线程池方法进行运算for(int i=0;i<M;i++)for(int j=0;j<N;j++)C[i][j]=0;//将C矩阵全置零//建立四个工作线程Thread []poolThreads=new Thread[NUM_THREADS];for(int i=0;i<NUM_THREADS;i++)poolThreads[i]=new Thread(new WorkThread(i,A,B,C));//建立线程池ExecutorService pool = Executors.newCachedThreadPool();long time5=System.currentTimeMillis();//记录开始时间for(int i=0;i<NUM_THREADS;i++)pool.execute(poolThreads[i]);//将四个工作线程放入线程池中执行pool.shutdown();//在线程池终止前允许执行以前提交的任务while (true) {              if (pool.isTerminated()) {                   break;              }        }//用一个死循环判断线程池是否执行完成long time6=System.currentTimeMillis();//记录结束时间//打印方法二使用的时间和矩阵C三个随机位置的值System.out.println("计算["+M+","+K+"]与["+K+","+N+"]阶矩阵乘法,线程池计算用时:"+(time6-time5)+"毫秒");System.out.println(C[rol[0]][col[0]]+" "+C[rol[1]][col[1]]+" "+C[rol[2]][col[2]]);}}

5.运行结果:


根据上述结果可以看出:并不是线程数量越多,运算速度越快!运算时间与许多因素有关,创建线程也需要时间!在一些小型运算中,创建线程的时间甚至比计算时间还长;另外,曾测试过在程序中将for循环修改成顺序一条条语句,运算时间降低了很多!在不同时间进行计算结果也不太一致~~



随着线程数量增多,运算时间反而越来越长~~
另外,不同的电脑可能不太一样!大家可以自己测试一下~~

下面我们来计算一下s:
选取4线程时的数据,speedup=7058/4815=1.465,电脑内核数n是4,带入公式中可以得到s<=57.7%,也就是程序中必须单线程执行的占所有的比例小于57.7%

0 0