关于大任务计算方法探索-----Java ForkJoin

本文以计算0~50000000000L累和为例,探索常用的几种方式所占用的CPU时间,当然耗用时间越少,说明性能越佳。
电脑配置为:i7-6700,16 GB内存,4核8线程。
1. java ForkJoin的invoke,采用任务对半分的方式。
2. 常规For循环。
3. java Forkjoin的invoke,采用平均分为8个任务方式。
4. 多线程Thread方式,采用平均分为8个任务方式,对应到每个线程里。

方式1和3的区别在于是否把任务合理分配到了8个CPU中。

class ForkJoinSumCalculate extends RecursiveTask<Long>{
    private long start;
    private long end;
    private long thurshold;   //可变临界值

    public ForkJoinSumCalculate(long start, long end, long thurshold){
        this.start = start;
        this.end = end;
        if(thurshold == 0){
            int cpus = Runtime.getRuntime().availableProcessors();
            if(cpus % 2 != 0){
                cpus++;
            }
            this.thurshold = (end-start)/cpus;
           // System.out.println("cpus="+cpus);
           // System.out.println(this.thurshold);
        }else{
            this.thurshold = thurshold;
        }
    }
    @Override
    protected Long compute() {
        long length = end-start;

        if(length <= thurshold){
            long sum = 0L;
            for (long i=start;i<=end;i++){
                sum += i;
            }
            return sum;
        }else{
            long middle = (start + end)/2;
            ForkJoinSumCalculate left = new ForkJoinSumCalculate(start,middle,thurshold);
            left.fork();
            ForkJoinSumCalculate right = new ForkJoinSumCalculate(middle+1,end,thurshold);
            right.fork();
            return left.join()+right.join();
        }
    }
}

class ForkJoinTaskSimple extends RecursiveTask<Long>  {
    private long start;
    private long end;
    private boolean forkMe;

    public ForkJoinTaskSimple(long start, long end, boolean forkMe) {
        super();
        this.end = end;
        this.start = start;
        this.forkMe = forkMe;
    }
    @Override
    protected Long compute() {
        if(!forkMe){
            long sum = 0L;
            for(long i=start;i<=end;i++){
                sum += i;
            }
            return sum;
        }else{
            long start,end,length;
            length = (this.end - this.start) / 8;
            start = this.start;
            end = length;
            ForkJoinTaskSimple task1 = new ForkJoinTaskSimple(start,end,false);
            start = end+1;
            end += length;
            ForkJoinTaskSimple task2 = new ForkJoinTaskSimple(start,end,false);
            start = end+1;
            end += length;
            ForkJoinTaskSimple task3 = new ForkJoinTaskSimple(start,end,false);
            start = end+1;
            end += length;
            ForkJoinTaskSimple task4 = new ForkJoinTaskSimple(start,end,false);
            start = end+1;
            end += length;
            ForkJoinTaskSimple task5 = new ForkJoinTaskSimple(start,end,false);
            start = end+1;
            end += length;
            ForkJoinTaskSimple task6 = new ForkJoinTaskSimple(start,end,false);
            start = end+1;
            end += length;
            ForkJoinTaskSimple task7 = new ForkJoinTaskSimple(start,end,false);
            start = end+1;
            end = this.end;
            ForkJoinTaskSimple task8 = new ForkJoinTaskSimple(start,end,false);
            task1.fork();
            task2.fork();
            task3.fork();
            task4.fork();
            task5.fork();
            task6.fork();
            task7.fork();
            task8.fork();
            return task1.join()+task2.join()+task3.join()+task4.join()+task5.join()+task6.join()+task7.join()+task8.join();
        }

    }

}
class MultiThreadTask extends Thread{
    private long s;
    private long e;
    public long  sum;

    public MultiThreadTask(long start, long end) {
        s = start;
        e = end;
        sum = 0;
    }

    @Override
    public synchronized void start() {
        super.start();
    }

    @Override
    public void run() {
        for(long i=s;i<e;i++){
            sum += i;
        }
    }
}

public class ForkJoinPoolTest {
    public void run(){  //8秒左右
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinSumCalculate(0,50000000000L,0);
        Instant start = Instant.now();
        long sum = pool.invoke(task);
        Instant end = Instant.now();
        System.out.println(sum);
        System.out.println("使用ForkJoin,耗费时间为:"+Duration.between(start,end).toMillis());
    }
    public void normal(){   //13秒左右
        long sum =0L;
        Instant start = Instant.now();
        for(long i=0;i<50000000000L;i++){
            sum += i;
        }
        Instant end = Instant.now();
        System.out.println(sum);
        System.out.println("使用单线程循环,耗费时间为:"+Duration.between(start,end).toMillis());
    }
   public void divide(){    //11秒左右
       ForkJoinPool pool = new ForkJoinPool();
       ForkJoinTask<Long> task = new ForkJoinTaskSimple(0,50000000000L,true);
       Instant start = Instant.now();
       long sum = pool.invoke(task);
       Instant end = Instant.now();
       System.out.println(sum);
       System.out.println("使用JorkJoin,但不是二分法分任务,耗费时间为:"+Duration.between(start,end).toMillis());
   }
   private long multiTheadInternal(long start,long end){
        long length = (end-start)/8;
        long tao = 0;
        tao += length;
        Thread thread1 = new MultiThreadTask(start,tao);
        start = tao+1;
        tao += length;
       Thread thread2 = new MultiThreadTask(start,tao);
       start = tao+1;
       tao += length;
       Thread thread3 = new MultiThreadTask(start,tao);
       start = tao+1;
       tao += length;
       Thread thread4 = new MultiThreadTask(start,tao);
       start = tao+1;
       tao += length;
       Thread thread5 = new MultiThreadTask(start,tao);
       start = tao+1;
       tao += length;
       Thread thread6 = new MultiThreadTask(start,tao);
       start = tao+1;
       tao += length;
       Thread thread7 = new MultiThreadTask(start,tao);
       start = tao+1;
       tao = end;
       Thread thread8 = new MultiThreadTask(start,tao);
       thread1.start();
       thread2.start();
       thread3.start();
       thread4.start();
       thread5.start();
       thread6.start();
       thread7.start();
       thread8.start();

       try {
           thread1.join();
           thread2.join();
           thread3.join();
           thread4.join();
           thread5.join();
           thread6.join();
           thread7.join();
           thread8.join();
       }catch (InterruptedException e){
           e.printStackTrace();
       }

       return ((MultiThreadTask) thread1).sum+((MultiThreadTask) thread2).sum+((MultiThreadTask) thread3).sum+((MultiThreadTask) thread4).sum+((MultiThreadTask) thread5).sum+
               ((MultiThreadTask) thread6).sum+((MultiThreadTask) thread7).sum+((MultiThreadTask) thread8).sum;
   }
    public void multiThread (){ //约为5秒
        Instant start = Instant.now();
        long sum = multiTheadInternal(0,50000000000L);
        Instant end = Instant.now();
        System.out.println(sum);
        System.out.println("使用多线程,耗费时间为:"+Duration.between(start,end).toMillis());
    }
}

运行结果如下:

CPU Intel i7-6700
-4378596987249509888
使用ForkJoin,耗费时间为:7829
-4378596987249509888
使用单线程循环,耗费时间为:14104
-4378596987249509888
使用JorkJoin,但不是二分法分任务,耗费时间为:11666
-4378596987249509888
使用多线程,耗费时间为:4669

从上述结果中可以看出,其实多线程来合作处理大任务要比多进程处理大任务较好些,更比单线程好得多。以上测试均考虑最大任务数为CPU线程数,即尽量合理分配任务到CPU上。

添加使用java8 求和

public void java8(){
    Instant start = Instant.now();
    long sum = LongStream.rangeClosed(0,50000000000L).parallel().reduce(0L,Long::sum);
    Instant end = Instant.now();
    System.out.println(sum);
    System.out.println("使用java8,耗费时间为:"+Duration.between(start,end).toMillis());
}

结果:

CPU Intel i7-6700
-4378596987249509888
使用java8,耗费时间为:5235

此时java8的求和仍然比上述多线程多花了近1秒的时间。


修改部分,以支持可变线程数:

class ForkJoinSumCalculate extends RecursiveTask<Long>{
    private long start;
    private long end;
    private long thurshold;   //可变临界值
    private  final static double MAGNIFICATION = 2;   //此处倍率用于调节任务细分程度,下同
    public ForkJoinSumCalculate(long start, long end, long thurshold){
        this.start = start;
        this.end = end;
        if(thurshold == 0){
            int cpus = Runtime.getRuntime().availableProcessors();
            cpus = (int)(cpus * MAGNIFICATION);
            if(cpus % 2 != 0){
                cpus++;
            }
            this.thurshold = (end-start)/cpus;
           // System.out.println("cpus="+cpus);
           // System.out.println(this.thurshold);
        }else{
            this.thurshold = thurshold;
        }
    }
    @Override
    protected Long compute() {
        long length = end-start;

        if(length <= thurshold){
            long sum = 0L;
            for (long i=start;i<=end;i++){
                sum += i;
            }
            return sum;
        }else{
            long middle = (start + end)/2;
            ForkJoinSumCalculate left = new ForkJoinSumCalculate(start,middle,thurshold);
            left.fork();
            ForkJoinSumCalculate right = new ForkJoinSumCalculate(middle+1,end,thurshold);
            right.fork();
            return left.join()+right.join();
        }
    }
}

class ForkJoinTaskSimple extends RecursiveTask<Long>  {
    private long start;
    private long end;
    private boolean forkMe;
    private  final static double MAGNIFICATION = 2;

    public ForkJoinTaskSimple(long start, long end, boolean forkMe) {
        super();
        this.end = end;
        this.start = start;
        this.forkMe = forkMe;
    }
    @Override
    protected Long compute() {
        if(!forkMe){
            long sum = 0L;
            for(long i=start;i<=end;i++){
                sum += i;
            }
            return sum;
        }else{
            long start,end,length;
            int cpus = Runtime.getRuntime().availableProcessors();
            long sum;
            cpus = (int)(cpus * MAGNIFICATION);
            length = (this.end - this.start) / cpus;
            start = this.start;
            end = 0;
            ArrayList<ForkJoinTaskSimple> joinTaskSimples = new ArrayList<ForkJoinTaskSimple>();

            for(int i=0;i<cpus;i++){
                if(i == (cpus - 1)){
                    end = this.end;
                }else{
                    end += length;
                }
                ForkJoinTaskSimple joinTaskSimple = new ForkJoinTaskSimple(start,end,false);
                joinTaskSimple.fork();
                start = end+1;
                joinTaskSimples.add(joinTaskSimple);
            }
            sum = 0;
            for(ForkJoinTaskSimple joinTaskSimple : joinTaskSimples){
                sum += joinTaskSimple.join();
            }
            return sum;
        }
    }

}
class MultiThreadTask extends Thread{
    private long s;
    private long e;
    public long  sum;

    public MultiThreadTask(long start, long end) {
        s = start;
        e = end;
        sum = 0;
    }

    @Override
    public synchronized void start() {
        super.start();
    }

    @Override
    public void run() {
        for(long i=s;i<=e;i++){
            sum += i;
        }
    }
}

public class ForkJoinPoolTest {
    public void run(){  //8秒左右
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinSumCalculate(0,50000000000L,0);
        Instant start = Instant.now();
        long sum = pool.invoke(task);
        Instant end = Instant.now();
        System.out.println(sum);
        System.out.println("使用ForkJoin,耗费时间为:"+Duration.between(start,end).toMillis());
    }
    public void normal(){   //13秒左右
        long sum =0L;
        Instant start = Instant.now();
        for(long i=0;i<=50000000000L;i++){
            sum += i;
        }
        Instant end = Instant.now();
        System.out.println(sum);
        System.out.println("使用单线程循环,耗费时间为:"+Duration.between(start,end).toMillis());
    }
   public void divide(){    //11秒左右
       ForkJoinPool pool = new ForkJoinPool();
       ForkJoinTask<Long> task = new ForkJoinTaskSimple(0,50000000000L,true);
       Instant start = Instant.now();
       long sum = pool.invoke(task);
       Instant end = Instant.now();
       System.out.println(sum);
       System.out.println("使用JorkJoin,但不是二分法分任务,耗费时间为:"+Duration.between(start,end).toMillis());
   }
    private  final static double MAGNIFICATION = 2;
   private long multiTheadInternal(long start,long end){
        long length;
        long tao = 0;
        int cpus = Runtime.getRuntime().availableProcessors();
       cpus = (int)(cpus * MAGNIFICATION);
       length = (end - start) / cpus;
        ArrayList<MultiThreadTask> threads = new ArrayList<MultiThreadTask>();
        for(int i=0;i<cpus;i++){
            if(i == (cpus - 1)){
                tao = end;
            }else {
                tao += length;
            }
            MultiThreadTask thread = new MultiThreadTask(start,tao);
            thread.start();
            threads.add(thread);
            start = tao+1;
        }
       long sum = 0;
        for(MultiThreadTask threadTask : threads ){
            try {
                threadTask.join();
                sum += threadTask.sum;
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        return sum;
   }
    public void multiThread (){ //约为5秒
        Instant start = Instant.now();
        long sum = multiTheadInternal(0,50000000000L);
        Instant end = Instant.now();
        System.out.println(sum);
        System.out.println("使用多线程,耗费时间为:"+Duration.between(start,end).toMillis());
    }
    public void java8(){
        Instant start = Instant.now();
        long sum = LongStream.rangeClosed(0,50000000000L).parallel().reduce(0L,Long::sum);
        Instant end = Instant.now();
        System.out.println(sum);
        System.out.println("使用java8,耗费时间为:"+Duration.between(start,end).toMillis());
    }
}

结果如下:

CPU Intel i7-6700
-4378596987249509888
使用ForkJoin,耗费时间为:6920
-4378596987249509888
使用单线程循环,耗费时间为:13314
-4378596987249509888
使用JorkJoin,但不是二分法分任务,耗费时间为:6614
-4378596987249509888
使用多线程,耗费时间为:4700
-4378596987249509888
使用java8,耗费时间为:5123

试验中发现,调节倍率,使得任务数多于CPU实际线程数在一定程度上能加快求解速度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值