android executorservice多线程性能蒙特卡罗估计

时间:2013-08-14 02:18:33

标签: java android multithreading

缺点:我正在尝试使用ExecutorService来运行多个线程,并且多个线程的计算时间太多更糟,而不是单个线程,所以我想弄清楚我在哪里出错。如果细节不重要,请跳过以下内容以获取代码。

很长一段时间:我设置了一个monte carlo方法来计算半径为1的圆的面积,以接近PI的值。我将其模拟为模拟用随机x和y坐标在圆上投掷飞镖。在大量的投掷中,圆圈的面积可以通过投掷多少飞镖来近似。

我创建了一个实现runnable的DartTosser类来抛出需要抛出的飞镖总数的增量。我创建了一个带有实例字段和同步方法的对象来聚合来自多个DartTossers的dart命中。一旦投掷了所有飞镖,估计并返回pi。

从用户输入读入线程数和投掷数。然后按下按钮会触发以下方法:

private double estimatePi(int numThreads, long numTosses, TossBin bin){
    long tossIncrement = numTosses/numThreads;
    ExecutorService executor = Executors.newFixedThreadPool(numThreads);

    for (int i = 0; i < numThreads; i++){
        executor.submit(new DartTosser(i, tossIncrement, bin));
    }

    executor.shutdown();

    System.out.println("All dart tossers have begun tossing darts.");

    try {
        executor.awaitTermination(1, TimeUnit.DAYS);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("All darts have been tossed.");

    return (4*bin.getTotalInCircle())/((double)numTosses);
}

DartTosser类在其run()方法中触发以下方法,然后将击中圆圈的总投掷添加到TossBin对象的实例中:

private long startTossing(){

    for (int i = 0; i < localTosses; i++){
        // get two random doubles between -1 and 1
        x = generateRandomNumber();
        y = generateRandomNumber();
        distanceSquared = (x*x) + (y*y);
        if (distanceSquared <= 1.0){
            tossesInCircle++;
        }
    }

    return tossesInCircle;
}

为了完整起见,这就是我的TossBin类:

public class TossBin {
    private long totalTossesInCircle = 0;

    public TossBin(){

    }

    public synchronized void addToTotalTosses(long n){
        this.totalTossesInCircle += n;
    }

    public void resetTossValues(){
        this.totalTossesInCircle = 0;
    }

    public long getTotalInCircle(){
        return totalTossesInCircle;
    }
}

PI近似值接近,所以我认为我的实际计算结果很好。运行单个线程,它将在大约1600毫秒左右处理100万次投掷。运行两个线程,它将在大约5600毫秒左右处理100万次投掷。线程越多,效率越低。我可以从系统输出看到线程(看起来是)并发运行,但我的并行化显然是各种各样的。

我在Galaxy S3上运行它以及eclipse中的模拟器,我看到了同样的趋势。有什么想法吗?

只是为了确保我播放了所有可能脏的床单:

public class DartTosser implements Runnable {

private int id;
private long localTosses = 0;
private long tossesInCircle = 0;
double x, y, distanceSquared;
TossBin globalBin;

private double generateRandomNumber(){
    return (Math.random()*2.0)-1.0;

}

public DartTosser(int id, long tosses, TossBin globalBin){
    this.id = id;
    this.localTosses = tosses;
    this.globalBin = globalBin;
}

private long startTossing(){

    for (int i = 0; i < localTosses; i++){
        // get two random doubles between -1 and 1
        x = generateRandomNumber();
        y = generateRandomNumber();
        distanceSquared = (x*x) + (y*y);
        if (distanceSquared <= 1.0){
            tossesInCircle++;
        }
    }

    return tossesInCircle;
}

@Override
public void run() {
    System.out.println("Dart tosser number " + Integer.toString(id) + "starting work.");

    globalBin.addToTotalTosses(startTossing());

    System.out.println("Dart tosser number " + Integer.toString(id) + "finished work.");
}

}

1 个答案:

答案 0 :(得分:2)

好的,这就是我的想法:

Math.random()使用Random的单个实例,使用nextDouble()调用本身是线程安全的compare-and-swap

因此,线程在生成随机数时会花费大量时间等待彼此。尝试为每个DartTosser实例提供自己的Random实例,并改为使用Random.nextDouble()