2013-08-14 3 views
0

Вперед: Я экспериментирую с ExecutorService для запуска нескольких потоков, а время вычислений много хуже с несколькими потоками, чем с одним потоком, поэтому я пытаюсь выяснить где я ошибся. Если детали не важны, пропустите длинную часть ниже, чтобы перейти к коду.android executorservice multithreading performance monte carlo оценка

Долгое время: я устанавливаю метод monte carlo вычисления площади круга с радиусом 1 для приближения значения PI. Я смоделировал его как симуляцию бросания дротиков на круг со случайными координатами x и y. При большом количестве бросков площадь круга может быть аппроксимирована количеством дротиков.

Я создал класс DartTosser, реализующий runnable, чтобы подбрасывать приращение общего количества дротиков, которые нужно бросить. Я создал объект с полем экземпляра и синхронизированный метод для агрегирования ударов дротиков из нескольких DartTossers. Когда все дротики были брошены, 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 близко, так что я думаю, что мои фактические расчеты хороши. Запустив один поток, он обработает 1 миллион томов примерно за 1600 миллисекунд или около того. Запустив два потока, он обработает 1 миллион томов примерно за 5600 миллисекунд или около того. Чем больше потоков, тем более неэффективно. Я могу видеть на выходе системы, что потоки (как представляется, работают) одновременно, но моя распараллеливание, очевидно, всевозможно разворачивается.

Я запускаю это на галактике S3, а также эмулятор от затмения, и я вижу ту же общую тенденцию. Есть предположения?

И просто чтобы убедиться, что я в эфире все мои потенциально грязных постельного белья:

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."); 
} 

} 
+0

Просто чтобы убедиться, вы вызываете метод 'addToTotalTosses' только тогда, когда один поток выполняется со всеми его выводами, не так ли? – Aert

+0

Да! Я должен продолжать печатать. – Deranger

+0

И вы не делитесь своим случайным генератором между потоками? – Aert

ответ

2

Хорошо, вот что я думаю, что происходит:

Math.random() использует один экземпляр Random, вызывая nextDouble() который сам по себе является потокобезопасным, используя compare-and-swap.

В результате нитки тратят много времени на ожидание друг друга при генерации случайных чисел. Попробуйте дать каждому экземпляру DartTosser свой собственный экземпляри вместо этого использовать Random.nextDouble().

+0

Это звучит разумно. Я проверю это, как только у меня появится шанс, и примите решение, разрешив проблему. Благодаря! – Deranger

+0

Чувак, ты понял. 2 миллиона бросков, 1 нить, 2000 миллисекунд. 2 миллиона бросков, 2 потока, 1000 миллисекунд. Roooock! – Deranger

+0

Sweeeeeeeeeeet! – Aert

Смежные вопросы