串行计算机与并行计算、并发计算的比较

原文转自:从串行到并行,从并行到分布式

1. 概念

假设有AB两个任务,则串行、并行、并发的区别如图1所示。

串行

A和B两个任务运行在一个CPU线程上,在A任务执行完之前不可以执行B。即,在整个程序的运行过程中,仅存在一个运行上下文,即一个调用栈一个堆。程序会按顺序执行每个指令。

并行

并行性指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同CPU上同时执行。比如,A和B两个任务可以同时运行在不同的CPU线程上,效率较高,但受限于CPU线程数,如果任务数量超过了CPU线程数,那么每个线程上的任务仍然是顺序执行的。

并发

并发指多个线程在宏观(相对于较长的时间区间而言)上表现为同时执行,而实际上是轮流穿插着执行,并发的实质是一个物理CPU在若干道程序之间多路复用,其目的是提高有限物理资源的运行效率。 并发与并行串行并不是互斥的概念,如果是在一个CPU线程上启用并发,那么自然就还是串行的,而如果在多个线程上启用并发,那么程序的执行就可以是既并发又并行的。

而分布式和并行的区别如下:

分布式

分布式在并行处理的基础上,强调任务正在执行的物理设备,如处理器、内存等等硬件,在物理上是分开的。而并行计算是指在一台计算机上的计算,在物理上不分开。

2. 例子

假设有A,B两个任务,任务A需要计算1-100000之间所有质数的和,任务B需要计算100001-200000之间所有质数的和

则采用串行的方法设计的程序如下:

public class Main {
    //判断是否为质数
    private static boolean isPrime(int n) {
        if(n < 2) return false;
        if(n == 2) return true;
        if(n%2==0) return false;
        for(int i = 3; i < n; i += 2)
            if(n%i == 0) return false;
        return true;
    }
    //串行计算
    private static void serial() {
        long time1 = System.currentTimeMillis(), time2,time3;
        long count = 0;
        for(int i=1;i<=100000;++i){
            if(isPrime(i)) count+=i;
        }
        time2=System.currentTimeMillis();
        System.out.println("1-100000之间质数和为"+count+" 耗时:"+(time2- time1) + "毫秒");
        count = 0;
        for(int i=100001;i<=200000;++i){
            if(isPrime(i))
                count+=i;
        }
        time3 = System.currentTimeMillis();
        System.out.println("100001-200000之间质数和为"+count+" 耗时:"+(time3 - time2) + "毫秒");
        System.out.println("总耗时:"+ (time3 - time1) + "毫秒");
    }
    //主函数
    public static void main(String[] args) {
        serial();
    }
}

在串行计算的程序中,只有一个CPU线程,且该线程按顺序执行AB两个任务。程序运行结果如下:

采用并发的方法设计的程序如下:

public class Main{
    private static boolean isPrime(int n) {
        if(n < 2) return false;
        if(n == 2) return true;
        if(n%2==0) return false;
        for(int i = 3; i < n; i += 2)
            if(n%i == 0) return false;
        return true;
    }
    public static void main(String[] args) {
        serialConcurrency();
    }
    private static void serialConcurrency() {
        long time = System.currentTimeMillis();
        //任务切换标识,1代表A任务,2代表B任务
        int task = 1;
        //计数器
        long count1 = 0, count2 = 0;
        int i=1,j=100001;
        while (true)
        {
            if(task == 1 && i++<=100000) {
                if(isPrime(i)) count1+=i;
                task = 2;
            }
            else if(task == 2 && j++<=200000) {
                if(isPrime(j)) count2+=j;
                task = 1;
            }
            else{
                break;
            }
        }
        System.out.println("1-100000之间质数和为"+count1);
        System.out.println("100001-200000之间质数和为"+count2);
        System.out.println("总耗时:"+(System.currentTimeMillis() - time) + "毫秒");
    }
}

在并发计算的程序中,同样只有一个CPU线程,但是该线程会在AB两个任务之间进行切换,可以发现,并发计算的总耗时反而大于串行计算,这是因为CPU在任务切换过程中需要消耗一定时间。程序运行结果如下:

采用并行的方法设计的程序如下:

public class Main {
    public static boolean isPrime(int n) {
        if(n < 2) return false;
        if(n == 2) return true;
        if(n%2==0) return false;
        for(int i = 3; i < n; i += 2)
            if(n%i == 0) return false;
        return true;
    }
    public static void main(String[] args) throws InterruptedException {
        long time1 = System.currentTimeMillis(),time2;
        Task task1 = new Task(1,100000);
        Task task2 = new Task(100001,200000);
        Thread thread1 = new Thread(task1);
        Thread thread2 = new Thread(task2);
        thread1.start();
        thread2.start();
        while (thread1.isAlive() || thread2.isAlive()){
            Thread.sleep(1);
        }
        time2 = System.currentTimeMillis();
        System.out.println("总耗时:"+(time2 - time1)+"毫秒");
    }
}
class Task implements Runnable{
    private int start;
    private int end;
    Task(int start, int end) {
        this.start = start;
        this.end = end;
    }
    public void run() {
        long time = System.currentTimeMillis();
        long count = 0;
        for(int i=start;i<=end;++i){
            if(Main.isPrime(i)) count+=i;
        }
        System.out.println(String.format("%d-%d之间质数和为%d,耗时:%d毫秒",start,end,count,(System.currentTimeMillis()- time)));
    }
}

 

在并行计算的程序中,AB任务各占用一个CPU线程,AB任务同时执行,总共耗费的时间约等于AB任务的最长耗时,程序运行结果如下:

3.总结

由上表可知并行的总耗时是最小的,效率最高(如果AB两个任务耗时更接近,则并行计算的效率将更高)。但由于并行计算受限于CPU线程数,当计算量超出单台计算机的计算能力时,人们就开始考虑使用多台计算机同时处理一个任务,分布式计算应用而生。分布式计算将任务分解成许多小的部分,分配给多台计算机进行处理,从而整体上节约了计算时间。Hadoop的MapReduce就是一种分布式计算框架,我们之后会对MapReduce进行详细的探讨。

  • 4
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
首先,计算π的方法主要有两种:蒙特卡罗方法和数学公式法。这里以数学公式法为例,介绍Java的串行、并行、并发计算π的实现方法。 1. 串行计算π 首先,我们可以使用莱布尼茨级数公式来计算π。该公式的计算公式为:π/4 = 1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 + ... + (-1)^(n+1)/(2n-1)。我们可以通过循环来计算这个公式,直到达到预设的精度。下面是一个Java串行计算π的示例代码: ``` public class SerialPi { public static void main(String[] args) { long start = System.currentTimeMillis(); int n = 100000000; // 循环次数 double sum = 0.0; for (int i = 1; i <= n; i++) { double item = (i % 2 == 1 ? 1 : -1) * 1.0 / (2 * i - 1); sum += item; } double pi = 4 * sum; long end = System.currentTimeMillis(); System.out.println("Pi: " + pi); System.out.println("Time: " + (end - start) + "ms"); } } ``` 2. 并行计算π 对于计算π这种密集型的计算任务,我们可以使用并行计算来提高计算效率。Java中提供了多线程的机制来实现并行计算。我们可以将计算任务分配给多个线程来同时进行计算,最后将结果进行合并。下面是一个Java并行计算π的示例代码: ``` public class ParallelPi { private static final int NTHREADS = 4; // 线程数量 private static final int N = 100000000; // 循环次数 private static double[] sumArray = new double[NTHREADS]; public static void main(String[] args) throws InterruptedException { long start = System.currentTimeMillis(); Thread[] threads = new Thread[NTHREADS]; for (int i = 0; i < NTHREADS; i++) { int id = i; threads[i] = new Thread(() -> { double sum = 0.0; for (int j = id; j < N; j += NTHREADS) { double item = (j % 2 == 1 ? 1 : -1) * 1.0 / (2 * j - 1); sum += item; } sumArray[id] = sum; }); threads[i].start(); } for (Thread thread : threads) { thread.join(); } double sum = 0.0; for (double item : sumArray) { sum += item; } double pi = 4 * sum; long end = System.currentTimeMillis(); System.out.println("Pi: " + pi); System.out.println("Time: " + (end - start) + "ms"); } } ``` 3. 并发计算π Java中的并发计算可以通过线程池来实现。线程池可以有效地管理线程的数量,避免线程创建和销毁的开销。下面是一个Java并发计算π的示例代码: ``` public class ConcurrentPi { private static final int NTHREADS = 4; // 线程数量 private static final int N = 100000000; // 循环次数 private static double[] sumArray = new double[NTHREADS]; public static void main(String[] args) throws InterruptedException, ExecutionException { long start = System.currentTimeMillis(); ExecutorService executor = Executors.newFixedThreadPool(NTHREADS); List<Future<Double>> futures = new ArrayList<>(); for (int i = 0; i < NTHREADS; i++) { int id = i; futures.add(executor.submit(() -> { double sum = 0.0; for (int j = id; j < N; j += NTHREADS) { double item = (j % 2 == 1 ? 1 : -1) * 1.0 / (2 * j - 1); sum += item; } return sum; })); } double sum = 0.0; for (Future<Double> future : futures) { sum += future.get(); } double pi = 4 * sum; long end = System.currentTimeMillis(); System.out.println("Pi: " + pi); System.out.println("Time: " + (end - start) + "ms"); executor.shutdown(); } } ``` 以上就是Java串行、并行、并发计算π的示例代码,希望能对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值