并行计算圆周率π

简单说明

除了“数据挖掘与机器学习”,学院还开了“分布与并行计算”这门课,同样也是要求我们计算机1、2班的同学都选修这门课。
采用并行的方式计算π,是这门课的第二个实验内容,看起来很简单,实际上也很简单,目的是分别记录单线程与多线程计算出π的时间,由时间的差别体验一下并行计算的威力。

描述

求π有个公式:

π/4 ≈ 1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 + ….

如果是单线程计算,当然是逐项加上去,而采用多线程来计算,就要考虑并行计算的并发性,这里应是数据分解。
假设采用4线程计算,那么有
Thread-0计算第1,5,9。。。项,
Thread-1计算第2,6,10。。。项,
Thread-2计算第3,7,11。。。项,
Thread-3计算第4,8,12。。。项,
直到各自计算达到相应的精度停止,最后将多个线程计算得的结果相加算出最终结果。

Java代码如下:
//CalcThread.java

/**
 * 独立的计算线程
 */
public class CalcThread extends Thread {

    private double result; //记录当前线程计算的结果
    private double from; //开始计算的项
    private double interval; //项数间隔
    private double accuracy; //计算的精度
    private double sign; //符号

    public CalcThread(double from, double interval, double accuracy) {
        this.from = from;
        this.interval = interval * 2;
        this.accuracy = accuracy;
        result = 0;
        sign = (from - 1)  / 2 % 2 == 0 ? 1 : -1;
    }

    @Override
    public void run() {
        double i = sign / from;
        while(Math.abs(i) >= accuracy){ //精度控制
            result += i;
            from += interval;
            sign = (from - 1)  / 2 % 2 == 0 ? 1 : -1;
            i = sign / from;
        }
    }

    /**
     * 取出该线程计算的结果
     * @return
     */
    public double getResult() {
        return result;
    }

}
//Main.java

public class Main {

    public static void main(String[] args) {
        double PI;
        long t1;
        long t2;

        System.out.println("单线程");
        t1 = System.currentTimeMillis(); //记录开始时间
        PI = createThread(1, 1e-9);
        System.out.println("PI = " + PI);
        t2 = System.currentTimeMillis();  //记录结束时间
        System.out.println("单线程时间: " + (1.0 * (t2 - t1) / 1000 + "秒"));
        System.out.println();

        System.out.println("2线程");
        t1 = System.currentTimeMillis();
        PI = createThread(2, 1e-9);
        System.out.println("PI = " + PI);
        t2 = System.currentTimeMillis();
        System.out.println("2线程时间: " + (1.0 * (t2 - t1) / 1000 + "秒"));
        System.out.println();

        System.out.println("4线程");
        t1 = System.currentTimeMillis();
        PI = createThread(4, 1e-9);
        System.out.println("PI = " + PI);
        t2 = System.currentTimeMillis();
        System.out.println("4线程时间: " + (1.0 * (t2 - t1) / 1000) + "秒");
    }

    private static double createThread(int threadCount, double accuracy) {
        double PI = 0;
        CalcThread[] threads = new CalcThread[threadCount];
        //准备线程
        for (int i = 0; i < threadCount; i++) {
            threads[i] = new CalcThread(2 * i + 1, threadCount, accuracy);
        }
        //启动线程
        for (int i = 0; i < threadCount; i++) {
            threads[i].start();
        }
        try {
            for (int i = 0; i < threadCount; i++) {
                //等待多线程计算,获取计算结果
                threads[i].join();
                PI += threads[i].getResult();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        PI *= 4;
        return PI;
    }

}
计算结果

精度 = 1e-6 :

单线程
PI = 3.141590653589692
单线程时间: 0.028秒

2线程
PI = 3.1415906535890485
2线程时间: 0.017秒

4线程
PI = 3.141590653590204
4线程时间: 0.008秒

精度 = 1e-7 :

单线程
PI = 3.1415924535897797
单线程时间: 0.215秒

2线程
PI = 3.141592453587034
2线程时间: 0.128秒

4线程
PI = 3.14159245359098
4线程时间: 0.082秒

精度 = 1e-8 :

单线程
PI = 3.1415926335902506
单线程时间: 2.268秒

2线程
PI = 3.1415926335840005
2线程时间: 1.377秒

4线程
PI = 3.141592633596291
4线程时间: 0.838秒

精度 = 1e-9 :

单线程
PI = 3.141592651589258
单线程时间: 22.815秒

2线程
PI = 3.1415926515851744
2线程时间: 13.034秒

4线程
PI = 3.1415926515922354
4线程时间: 8.086秒

相关问题
  1. 计算出来的π的结果不一样

    由于各个线程停止计算的控制都由同一精度来控制,所以会由线程数不同,导致所相加的公式中的总项数不同。

  2. 用不同线程数计算所消耗的时间差别不明显,甚至多线程耗时更多了

    同学之间讨论的大致有两种情况:
    一是在主线程用循环来等待各个线程计算结束。这种方式会在主线程白白消耗CPU资源来做判断,总耗时显然会更多。
    二是在线程之间用共享的变量来计算结果,然后加线程锁。这种方式相当于让多线程串行执行了,这样可能反而增加了系统在调度线程的消耗,也可能让耗时增多。
    这一问题还有一种情况是,老CPU只支持单线程执行,或者在单核单线程的CPU的虚拟机上跑,这样就跟上述第二中情况一样,相当于多线程串行执行,系统在调度线程上消耗了更多资源,总耗时增多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值