码迷,mamicode.com
首页 > 其他好文 > 详细

大规模素数的求解为题

时间:2015-03-16 06:09:45      阅读:164      评论:0      收藏:0      [点我收藏+]

标签:

问题原型:求三千万以内的素数.

问题的解决办法很简单,写一个求素数算法 然后循环就可以.这个是顺序执行方式,下面附上代码

public abstract class PrimeAbstract {
    public boolean isPrime(int i){
        if(i<=1)return false;
        else{
            for(int j=2; j<=Math.sqrt(i);j++){
                if(i%j == 0)return false;
            }
            return true;
        }
    }
    
    public static void print(Object a){
        System.out.println(a);
    }
    
    public int countPrimeInRange(int low, int up){
        int total  = 0;
        for(int i=low; i<=up; i++){
            if(isPrime(i))total++;
        }
        return total;
    }
    
    public void timeAndCompute(final int number){
        final long start  =System.nanoTime();
        final long num = countPrimes(number);
        final long end = System.nanoTime();
        print("小于"+number+"的素数的个数为"+num+"花费的时间"+(end-start)/1.0e9);
    }
    
    public abstract int countPrimes(final int max);
}

上面是一个抽象类,大致就是定义了几种求解素数的工具,求一个范围的素数,还有计时,还有求解整个范围素数的方法,而这个方法是抽象的也是我们需要实现的,也是并行和非并行的不同之处.

顺序执行的方法:

class primeshunxu extends PrimeAbstract{

    @Override
    public int countPrimes(int max) {
        // TODO Auto-generated method stub
        return countPrimeInRange(0, max);
    }
    
}

很简单,已经有现成的工具了嘛,直接用就好.

并行的方法:

并行的精髓是做任务分割,具体怎么分割上面其实已经表现出来了,讲整个求解范围进行分割,然后把求解结果累加即可.下面是代码

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;


public class Prime extends PrimeAbstract{
    

    @Override
    public int countPrimes(int max) {
        // TODO Auto-generated method stub
        final int poolsize = (int) (Runtime.getRuntime().availableProcessors());
        final ExecutorService executorpool = Executors.newFixedThreadPool(poolsize);
        final List<Callable<Integer>> partition = new ArrayList<>();
        int total = 0;
        int tasks  =poolsize;
        int per = max/tasks;
        int pre = 0;
        for(int j = 1; j<=tasks; j++){
            final int low = pre+1;
            final int up;
            if(low+per>max){
                up = max;
            }else{
                up = low+per;
            }
            partition.add(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    // TODO Auto-generated method stub
                    return countPrimeInRange(low, up);
                }
            });
            pre = up;
        }
        try {
            List<Future<Integer>> result = executorpool.invokeAll(partition);
            executorpool.shutdown();
            for(Future<Integer> i : result){
                total += i.get();
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return total;
    }

}

最后调用

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int max = 3000000;
        new Prime().timeAndCompute(max);
        new primeshunxu().timeAndCompute(max);
        
    }

现在我看看下运行对比

小于3000000的素数的个数为216816花费的时间6.464452757
小于3000000的素数的个数为216816花费的时间12.226215544

可以看到并行的时间是顺序执行的一半,虽然代码上复杂了很多,但是效果还是比较明显的,这里就涉及到了poolsize和tasks的个数问题:

根据poolsize计算公式,对于计算密集型任务,阻塞系数是0,那么poolsize大小设置成和cpu核数相等即可,因为假如设置大了以后,cpu就会挂起一个非阻塞线程,然后去执行另一个非阻塞任务,这样做意义不大,而且会代码额外的上下文切换开销.也就是在cpu密集型任务中,通过将线程数增多到比核数还多,是无效的.

那么剩下的就是tasks了.

我们首先看tasks = poolsize的情况,也就是将每个线程执行一个任务,然后就结束

看下分析结果

技术分享

执行输出

小于3000000的素数的个数为216816花费的时间6.24529681

可以看到 有四个线程,因为本机的核数为4,然后其中三个线程在运行完毕后进入了驻留状态,而还有一个线程从开始到最后一直在执行,也就是那个数值分配最大的那个线程,由于计算花费的时间更多,所以最好只有这单个线程执行了.所以运行时间也就是运行时间最长的那个线程所花的时间

那么我们可以想到,这样来分配任务的话,造成了四个线程的负载时不均衡的,计算量最大的线程是时间瓶颈,那么我们可以怎么做呢?

1.在任务数不变的情况下重新划分,可以想象前面12.5%的数计算量很小,后12.5%计算量很大,这前面的八分之一和后面的八分之一组合成一个任务,这样来进行均衡,但是这样做要对问题本身的行为要有一个非常深刻的理解,实现相对麻烦

2.增加任务数,我们可以想象,当我们任务数变多的时候,每次四个线程上跑的都是时间花销差不多的任务,这样一来,我们就能够做到任务均衡了.但是具体增加到多少呢?

我们增加到100个任务

技术分享

运行结果:

小于3000000的素数的个数为216816花费的时间5.921416693

可以看到,有了一些提升,而且每个线程从开始到最后都是满负载的,做到了负载均衡.这里虽然没有理论上的达到顺序执行的四分之一,也就是四秒左右,但是已经很客观了.

 

关于poolsize和tasks的总结:

1.子任务的划分数是不能小于处理器的核心数,否则性能都很差的.

2.对于计算密集型任务,线程数多于cpu核心数对性能提升是毫无帮助的.

3.在子任务数超过一定的数量后,再继续增加的话,对性能提升是十分有限的

 

大规模素数的求解为题

标签:

原文地址:http://www.cnblogs.com/color-my-life/p/4340912.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!