操作系统实验——串行、多线程和线程池三种方式计算矩阵乘法
2015-04-10 20:12
330 查看
操作系统实验——串行、多线程和线程池三种方式计算矩阵乘法
注:写实验报告的同学在复制代码的时候记得改一改变量名和函数!最好看懂了自己写一下!不要直接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; } @Override public 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;//矩阵A private static int [][]B;//矩阵B private static int [][]C;//矩阵C //--------------------- //驱动类构造方法 public Driver(){ A=new int[M][K]; B=new int[K] ; C=new int[M] ;//A、B、C初始化 fillRandom(A);//用0-99的随机数初始化矩阵A fillRandom(B);//用0-99的随机数初始化矩阵B for(int i=0;i<M;i++) for(int j=0;j<N;j++) C[i][j]=0;//将C矩阵全置零 } //------------------- //初始化方法:产生0-99的随机数初始化矩阵A、B private 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%
相关文章推荐
- 用多线程并发的方式来计算两个矩阵的乘法
- java 多线程并行计算之矩阵乘法继承Thread类实现(星星笔记)
- 动态规划 的方法求矩阵乘法的最少计算加括号方式
- java 多线程并行计算之矩阵乘法(星星笔记)
- 从矩阵乘法的不同计算方式来看局部性原理
- 多线程--Python下载(支持断点续传) & Java多线程计算矩阵乘法
- 【转】C/C++中 算法运行时间的三种计算方式
- C# 给多线程传参的三种方式
- C# 给多线程传参的三种方式
- 计算矩阵乘法的网页工具
- C/C++中算法运行时间的三种计算方式
- 矩阵乘法的四种理解方式
- 浅谈.NET下的多线程和并行计算(六)线程池基础下 (转)
- 一个直观的矩阵乘法的方式
- 分享矩阵乘法单线程与多线程的Java实现与效率对比,请教Strassen算法
- 汇编—双重循环—计算4*4矩阵与4*1矩阵乘法
- c++创建多线程的三种方式的比较 和 多线程通信
- C# 给多线程传参的三种方式
- 浅谈.NET下的多线程和并行计算(五)线程池基础上 (转)
- 矩阵乘法-分块计算